这是一个自我expaining的问题,但在这里我们去。 我创建Django的业务应用程序,我没有想以“传播”的所有逻辑跨应用程序和数据库,但在另一方面,我不希望让数据库完成这一任务(其可能通过使用触发器 )。
所以我想“复制”的DATABSE触发器的行为,但Model类中在Django(UM目前使用Django 1.4)。
经过一番研究,我想通了,与单个对象,我可以覆盖“models.Model”类的“保存”和“删除”的方法,“之前”和挂钩“之后”将让他们可以前后执行父母的保存/删除。 像这样:
class MyModel(models.Model):
def __before(self):
pass
def __after(self):
pass
@commit_on_success #the decorator is only to ensure that everything occurs inside the same transaction
def save(self, *args, *kwargs):
self.__before()
super(MyModel,self).save(args, kwargs)
self.__after()
最大的问题是批量操作。 Django不触发运行“更新()” /时,保存/模型的删除“删除()”,从它的查询集。 insted的,它使用的QuerySet自己的方法。 并得到一点点最差的,但它也不触发任何信号。
编辑:只是要更具体一点:视图里面的模型加载是动态的,所以这是不可能的定义“特定模型”的方式。 在这种情况下,我应该创建一个抽象类,并处理它。
我最后的尝试是创建一个自定义管理器,在这个定制经理,重载update方法,循环在查询集里面的模型,trigering“保存()”每个模型的(以考虑上面的实施,或“信号”系统)。 它的工作原理,但结果在数据库中“过载”(想象查询集正在更新10K行)。
随着几个注意事项,你可以重写查询集的update
方法火的信号,同时还使用SQL UPDATE
语句:
from django.db.models.signals import pre_save, post_save
def CustomQuerySet(QuerySet):
@commit_on_success
def update(self, **kwargs):
for instance in self:
pre_save.send(sender=instance.__class__, instance=instance, raw=False,
using=self.db, update_fields=kwargs.keys())
# use self instead of self.all() if you want to reload all data
# from the db for the post_save signal
result = super(CustomQuerySet, self.all()).update(**kwargs)
for instance in self:
post_save.send(sender=instance.__class__, instance=instance, created=False,
raw=False, using=self.db, update_fields=kwargs.keys())
return result
update.alters_data = True
我克隆当前查询集(使用self.all()
因为update
的方法将清除QuerySet对象的缓存。
有可能会或可能不会破坏你的代码的几个问题。 首先,它会引入一个竞争条件。 你做的事情pre_save
基于数据信号的接收器,当你更新数据库可能不再准确。
也可能有大的查询集一些严重的性能问题。 不像update
方法,所有车型必须加载到内存中,然后将信号仍然需要执行。 特别是,如果信号本身必须与数据库交互,性能可能慢得不可接受。 而不像常规pre_save信号,改变模型实例不会自动导致数据库进行更新,因为模型实例不用于保存新的数据。
可能有一些问题,将在几个边缘的情况下会导致问题。
无论如何,如果你能处理这些问题,而无需一些严重的问题,我觉得这是做到这一点的最好办法。 它产生的小的开销,同时尽可能仍然加载模型到内存中,这是非常需要正确地执行各种信号。
首先,并不是覆盖保存添加__before
和__after
方法,你可以使用内置的pre_save
, post_save,
pre_delete,
并post_delete
信号。 https://docs.djangoproject.com/en/1.4/topics/signals/
from django.db.models.signals import post_save
class YourModel(models.Model):
pass
def after_save_your_model(sender, instance, **kwargs):
pass
# register the signal
post_save.connect(after_save_your_model, sender=YourModel, dispatch_uid=__file__)
pre_delete
和post_delete
当你调用会得到触发delete()
上查询集。
对于批量更新,你将不得不但是手动调用要触发自己的功能。 你可以把它所有的交易为好。
,如果你正在使用动态模型调用适当的触发功能,您可以检查模型的ContentType。 例如:
from django.contrib.contenttypes.models import ContentType
def view(request, app, model_name, method):
...
model = get_model(app, model_name)
content_type = ContentType.objects.get_for_model(model)
if content_type == ContenType.objects.get_for_model(YourModel):
after_save_your_model(model)
elif content_type == Contentype.objects.get_for_model(AnotherModel):
another_trigger_function(model)