Django信号与重写保存方法

117 投票
5 回答
28760 浏览
提问于 2025-04-11 09:26

我现在有点搞不清楚状况。我的模型大概是这样的:

 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 个回答

5

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

请记住,这个操作会尽可能地在SQL层面执行,所以在这个过程中,单个对象的删除方法可能不会被调用。如果你在模型类中提供了一个自定义的删除方法,并且想确保它被调用,那么你需要“手动”删除这些模型的实例(比如,遍历一个QuerySet,然后对每个对象单独调用delete()),而不是使用QuerySet的批量删除方法。

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

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

最后要明白,update()是在SQL层面进行更新的,因此不会调用你模型中的任何保存方法,也不会触发预保存或后保存的信号(这些是调用Model.save()时产生的)。如果你想更新一个有自定义保存方法的模型的多个记录,就需要遍历这些记录并调用save()。

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

27

你问了:

使用Django的信号调度器有什么好处吗?

我在Django的文档中找到了这些内容:

在批量操作时,重写的模型方法不会被调用。

注意,当使用QuerySet批量删除对象或进行级联删除时,某个对象的delete()方法不一定会被调用。为了确保自定义的删除逻辑能够执行,你可以使用pre_delete和/或post_delete信号。

不幸的是,在批量创建或更新对象时没有解决办法,因为save()、pre_save和post_save都不会被调用。

来源:重写预定义模型方法

98

保存和删除信号通常在你需要进行一些更改时很有用,这些更改并不完全特定于某个模型,或者可以应用于一些有共同点的模型,或者可以配置为跨模型使用。

在重写的 save 方法中,一个常见的任务是自动生成“slug”(一种简化的文本标识符),通常是从模型中的某个文本字段生成。如果你需要为多个模型实现这个功能,使用 pre_save 信号会很有帮助,这样信号处理器就可以获取要生成 slug 的字段名和 slug 字段的名称。一旦你有了这样的设置,任何你添加的增强功能也会适用于所有模型,比如在添加 slug 之前检查一下这个 slug 是否在当前模型中是唯一的。

可重用的应用程序通常会从使用信号中受益——如果它们提供的功能可以应用于任何模型,通常(除非没有办法)它们不希望用户直接修改自己的模型来享受这些功能。

例如,在使用 django-mptt 时,我使用了 pre_save 信号来管理一组描述树结构的字段,这些字段用于即将创建或更新的模型;同时使用 pre_delete 信号来删除被删除对象及其整个子树的树结构信息。由于使用了信号,用户不需要在自己的模型中添加或修改 savedelete 方法,django-mptt 会自动为他们管理这些,只需告诉 django-mptt 他们希望管理哪些模型即可。

撰写回答