【问题标题】:Why is post_save being raised twice during the save of a Django model?为什么在保存 Django 模型期间 post_save 会被提升两次?
【发布时间】:2011-01-21 16:05:48
【问题描述】:

我将一个方法附加到我的 Django 模型的 post_save 信号。这样,每当修改模型时,我都可以清除一些缓存的项目。

我遇到的问题是保存模型时信号被触发了两次。它不一定会伤害任何东西(代码只会优雅地出错),但它不可能是正确的。

一个简单的例子,只是将模型打印到控制台(使用开发服务器):

from blog.models import Post
from django.db.models import signals

def purge_cache(sender, **kwargs):
    print 'Purging %s' % sender

signals.post_save.connect(purge_cache, sender=Post)

这是使用 Django 的稳定 1.1.1 版本。

更新信息:

根据每个 cmet 的反馈,我修改了我的问题,因为问题现在发现了为什么 post_save 被触发两次。我目前的猜测是我的 models.py 代码被导入了两次,并且 post_save 被多次连接。

找出它为什么被导入/运行两次的最佳方法是什么?

【问题讨论】:

  • 如果注释掉post_delete 连接线,post_save 信号是否会引发两次?
  • 查看 django 代码,它确实没有发送 save(),你确定你收到的是 post_delete 信号而不是 post_save 两次?
  • @Desintegr - 我目前无法尝试,但这是个好主意。我今晚会尝试并更新问题。如果在删除 post_delete 后仍然出现这种情况,我将把我的问题改为“为什么 post_save 信号会出现两次?” :) 也许 models.py 代码运行了两次,信号连接了多次?
  • 是的,models.py 可以导入几次,例如在您自己的导入中。我认为这是一个常见问题:)
  • @Dmitry & Desintegr - 我认为你们俩都是对的。删除 post_delete 仍然会触发清除两次。我已更改问题以匹配新问题。

标签: python django django-models signals


【解决方案1】:

显然,Python is sensitive to the way you import modules。就我而言,这不是我的博客应用程序中的任何导入代码的问题,而是 INSTALLED_APPS 配置的问题,我假设 Django 使用该配置进行初始导入。

在我的博客应用程序中,我使用了以下导入:

from blog.models import *

我的 settings.py 被配置为:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    ...snip...
    'sorl.thumbnail',
    'mysite.blog',
)

添加“mysite”前缀是因为我最初在部署站点时遇到了导入路径问题。后来我通过在我的 WSGI 脚本中添加多个路径来解决这个问题(因此它与开发服务器的行为相同)。

从 settings.py 中删除“mysite”前缀解决了这个问题:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    ...snip...
    'sorl.thumbnail',
    'blog',
)

【讨论】:

    【解决方案2】:

    这是关于这个问题的票:Django's signal framework may register listeners more than once #3951。它现在已在 Django 的 SVN 版本中修复。

    问题正如您所说:您注册信号的模块被加载了几次,在某些情况下通过不同的导入路径,因此每个导入的模块都被 Django 错误地解释为注册相同信号的不同模块.

    【讨论】:

    • 我在使用 django 1.6 时仍然存在这个问题。提供 dispatch_uid 没有帮助。
    【解决方案3】:

    在寻找此问题的根源时,您可以使用快速解决方法来防止两次注册信号:

    signals.post_save.connect(my_handler, MyModel, dispatch_uid="path.to.this.module")
    

    Source.

    【讨论】:

    • 我见过这个,但这看起来真的像是他们添加的一个 hack,因为人们不会费心去修复他们的导入。不过感谢您的帮助!
    • 我想我不同意上面的@LanceMcNearney。假设您正在编写库代码。您无法控制消费者导入代码的方式。
    • 嗯 django 以其向后兼容性而闻名,此解决方案允许较新版本避免该问题并仍保留旧版本的行为
    • 我也不同意上面的@LanceMcNearney。在我的情况下,来自 v1.6 的测试运行程序导致了问题(在 1.5 中不是问题,即使我在那里也使用了 DiscoverTestRunner)。 dispatch_uid 似乎是更好的选择。
    • 这对我没有帮助。
    猜你喜欢
    • 2023-03-31
    • 2012-02-13
    • 2019-10-06
    • 2016-10-03
    • 1970-01-01
    • 2020-05-18
    • 2011-02-13
    • 2020-11-29
    • 1970-01-01
    相关资源
    最近更新 更多