这是一个自我贬低的问题,但我们走了。 我在Django创建了一个商业应用程序,我并不想"传播"应用程序和数据库中的所有逻辑,但另一方面,我不想让数据库处理此任务(通过使用Triggers可能)。
所以我想"重现"数据库触发器的行为,但在Django的模型类中(嗯当前正在使用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()
BIG问题在于批量操作。当运行" update()" /" delete()"时,Django不会触发模型的保存/删除。来自它的QuerySet。 Insted,它使用QuerySet自己的方法。最糟糕的是,它也不会触发任何信号。
修改: 更具体一点:视图中的模型加载是动态的,因此无法定义特定模型"办法。在这种情况下,我应该创建一个抽象类并在那里处理它。
我的最后一次尝试是创建一个自定义管理器,并在此自定义管理器中覆盖更新方法,循环查询集内的模型,然后触发" save()"每个模型(考虑上面的实现,或"信号"系统)。它有效,但会导致数据库超载" (想象一下10k行的查询集正在更新)。
答案 0 :(得分:1)
首先,您可以使用内置的__before
,__after
pre_save
和{{{}来代替保存以添加post_save,
和pre_delete,
方法。 1}}信号。 https://docs.djangoproject.com/en/1.4/topics/signals/
post_delete
当您在查询集上调用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
将被触发。
对于批量更新,您必须手动调用自己想要触发的功能。你也可以在交易中把它全部扔掉。
如果您正在使用动态模型,要调用正确的触发功能,您可以检查模型的ContentType。例如:
delete()
答案 1 :(得分:0)
有几点需要注意,您可以覆盖查询集的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信号不同,更改模型实例不会自动导致数据库更新,因为模型实例不用于保存新数据。
可能还有一些问题会在一些边缘情况下引起问题。
无论如何,如果你能解决这些问题而不会遇到一些严重的问题,我认为这是最好的方法。它在产生尽可能少的开销的同时仍将模型加载到存储器中,这对于正确执行各种信号几乎是必需的。