为什么在Django模型保存时post_save被调用两次?
我在我的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 个回答
这里有一个关于这个问题的记录:Django的信号框架可能会重复注册监听器 #3951。这个问题在Django的SVN版本中已经修复了。
问题正如你所说的那样:你的模块在注册信号时,被加载了几次,有时候是通过不同的导入路径加载的,因此Django错误地把这些通过这种方式导入的模块当成了不同的模块,结果导致它们都注册了相同的信号。
在寻找这个问题的根源时,你可以使用一个简单的临时解决办法,来防止信号被注册两次:
signals.post_save.connect(my_handler, MyModel, dispatch_uid="path.to.this.module")
来源.
显然,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',
)