Django 信号与覆盖保存方法

Posted

技术标签:

【中文标题】Django 信号与覆盖保存方法【英文标题】:Django signals vs. overriding save method 【发布时间】:2010-09-15 06:45:27 【问题描述】:

我很难理解这一点。现在我有一些看起来像这样的模型:

 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 是分数的平均值。保存评论或分数时,我需要重新计算总分平均值。现在我正在使用覆盖的保存方法。使用 Django 的信号调度器有什么好处吗?

【问题讨论】:

【参考方案1】:

保存/删除信号通常适用于您需要进行不完全特定于相关模型的更改,或者可以应用于具有共同点的模型,或者可以配置为跨模型使用的情况.

覆盖 save 方法的一个常见任务是从模型中的某些文本字段自动生成 slug。这是一个示例,如果您需要为多个模型实现它,将受益于使用 pre_save 信号,其中信号处理程序可以采用 slug 字段的名称和字段名称来生成蛞蝓来自。一旦你有了类似的东西,你放置的任何增强功能也将适用于所有模型 - 例如。查找您要为相关模型类型添加的 slug,以确保唯一性。

可重用应用程序通常受益于信号的使用 - 如果它们提供的功能可以应用于任何模型,它们通常(除非不可避免)不希望用户必须直接修改其模型才能从中受益.

以django-mptt 为例,我使用pre_save 信号来管理一组字段,这些字段描述了即将创建或更新的模型的树结构,并使用pre_delete 信号来删除树结构正在删除的对象及其之前的对象的整个子树的详细信息,并且它们已被删除。由于使用了信号,用户不必在他们的模型上添加或修改savedelete 方法来为他们完成这种管理,他们只需要让 django-mptt 知道他们想要哪些模型管理。

【讨论】:

如果信号处理程序触发异常怎么办?我想他们不应该触发异常,否则他们不合适。我错了吗?【参考方案2】:

你问:

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

我在 django 文档中找到了这个:

在批量操作中不调用被覆盖的模型方法

请注意,对象的 delete() 方法不一定会被调用 使用 QuerySet 批量删除对象时或由于 级联删除。为确保执行自定义删除逻辑,您 可以使用 pre_delete 和/或 post_delete 信号。

很遗憾,在创建或更新时没有解决方法 批量对象,因为 save()、pre_save 和 post_save 都不是 调用。

发件人:Overriding predefined model methods

【讨论】:

Django 管理员列表视图使用批量删除...一直很困惑,直到遇到这个花絮。 它还说“不幸的是,在批量创建或更新对象时没有解决方法,因为没有调用 save()、pre_save 和 post_save。” - 所以我不认为这是这些方法之间的权衡。 这适用于这两种方法,所以答案是:“不,与覆盖 save 方法相比,使用信号没有任何好处”?【参考方案3】:

如果您将使用信号,您将能够在每次保存相关分数模型时更新评论分数。但是,如果不需要这样的功能,我认为没有任何理由将其放入信号中,那是与模型相关的东西。

【讨论】:

【参考方案4】:

这是一种非规范化。看看这个pretty solution。就地组合字段定义。

【讨论】:

【参考方案5】:

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

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

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

【讨论】:

这不是对两者都适用吗?

以上是关于Django 信号与覆盖保存方法的主要内容,如果未能解决你的问题,请参考以下文章

如何以 django 模型形式覆盖保存方法

Django - 覆盖保存方法时检查旧值和新值之间的差异

此覆盖保存方法的 Django IntegrityError

在Django中覆盖保存方法时的无限循环

Django覆盖保存

Django - 覆盖模型保存()