为什么在Django模型保存时post_save被调用两次?

17 投票
3 回答
7623 浏览
提问于 2025-04-15 19:47

我在我的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。

更新信息:

根据大家的反馈,我修改了我的问题,因为现在我想弄清楚为什么post_save会被触发两次。我现在的猜测是我的models.py代码被导入了两次,导致post_save被连接了多次。

有什么好的方法可以找出为什么它会被导入/运行两次吗?

3 个回答

1

这里有一个关于这个问题的记录:Django的信号框架可能会重复注册监听器 #3951。这个问题在Django的SVN版本中已经修复了。

问题正如你所说的那样:你的模块在注册信号时,被加载了几次,有时候是通过不同的导入路径加载的,因此Django错误地把这些通过这种方式导入的模块当成了不同的模块,结果导致它们都注册了相同的信号。

9

在寻找这个问题的根源时,你可以使用一个简单的临时解决办法,来防止信号被注册两次:

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

来源.

13

显然,Python 对导入模块的方式很敏感。在我的情况下,问题并不出在我博客应用里的导入代码,而是出在 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',
)

撰写回答