Django信号与覆盖保存方法的区别

116

我对这个问题感到困惑。目前,我的一些模型看起来有点像这样:

 def Review(models.Model)
    ...fields...
    overall_score = models.FloatField(blank=True)

def Score(models.Model)
    review = models.ForeignKey(Review)
    question = models.TextField()
    grade = models.IntegerField()

评论包含多个“分数”,overall_score是这些分数的平均值。当保存评论或分数时,需要重新计算overall_score平均值。目前我正在使用重写的保存方法。使用Django信号分派器有什么好处吗?

5个回答

98

保存/删除信号通常在需要进行更改但不完全针对特定模型的情况下使用,或者可以应用于具有共同点的模型,或可以配置为跨模型使用。

重写save方法中的一个常见任务是从模型中的某个文本字段自动生成slug。如果您需要为多个模型实现此功能,则可以受益于使用pre_save信号,其中信号处理程序可以接收slug字段的名称以及要从中生成slug的字段的名称。一旦设置了类似于这样的内容,您增强的任何功能也将适用于所有模型,例如查找要添加的slug,以确保其唯一性。

可重用的应用程序通常受益于信号的使用 - 如果它们提供的功能可以应用于任何模型,那么它们通常(除非无法避免)不希望用户直接修改其模型以获得这些功能。

例如,在django-mptt中,我使用pre_save信号来管理描述即将创建或更新的模型的树结构的一组字段,并使用pre_delete信号来删除正在删除的对象及其整个子树之前和它们被删除的树结构细节。由于使用信号,用户无需添加或修改其模型上的savedelete方法即可进行此管理,他们只需要让django-mptt知道他们想要管理哪些模型。


如果信号处理程序触发异常会怎样?我认为它们不应该触发异常,否则它们就不适合了。我错了吗? - x-yuri

27
你问的是:使用Django信号分发器会有什么好处?
我在 Django 文档中找到了这个:
覆盖的模型方法不会在批量操作中被调用
请注意,使用 QuerySet 批量删除对象或作为级联删除结果时,不一定会调用对象的 delete() 方法。为确保执行自定义的删除逻辑,您可以使用 pre_delete 和/或 post_delete 信号。
不幸的是,在批量创建或更新对象时没有解决方法,因为 save()、pre_save 和 post_save 都没有被调用。
来源:覆盖预定义模型方法

3
Django的管理员列表视图使用批量删除...直到我找到这个提示,我一直感到困惑。 - N.Balauro
16
它还说:“不幸的是,当批量创建或更新对象时,没有绕过方法,因为不会调用save()、pre_save和post_save中的任何一个。”- 因此,我认为这并不是这些方法之间的权衡。 - Cory
3
这适用于两种方法,那么答案是:“不,相较于覆盖save方法而言,使用信号没有任何好处”? - Flimm

5

Django文档中有关批量删除(在QuerySet对象上使用.delete()方法)的小补充:

请注意,这将尽可能地纯粹在SQL中执行,因此在此过程中不一定会调用单个对象实例的delete()方法。如果您在模型类上提供了自定义delete()方法并希望确保它被调用,则需要“手动”删除该模型的实例(例如,通过迭代查询集并在每个对象上调用delete())而不是使用QuerySet的批量delete()方法。

https://docs.djangoproject.com/en/1.11/topics/db/queries/#deleting-objects

还有批量更新(在QuerySet对象上使用.update()方法):

最后,请注意,update()在SQL级别上执行更新,因此不会调用任何模型的save()方法,也不会发出pre_save或post_save信号(这是调用Model.save()的结果)。如果要为具有自定义save()方法的模型更新一堆记录,请对它们进行循环并调用save()。

https://docs.djangoproject.com/en/2.1/ref/models/querysets/#update


1
这不是两者都适用吗? - Flimm

3
如果您使用信号,每次相关的得分模型保存时都可以更新评论得分。但是如果不需要这样的功能,我认为没有理由将其放入信号中,因为这是相当与模型相关的内容。

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接