Django - post_init信号在模型实例保存时调用,甚至在实例创建之前,为什么?
我正在尝试写一个小应用程序,用来接收视频文件,并在上传后将它们转换成统一的格式(这样就可以添加到数据库里)。我在网上寻找最佳解决方案,决定使用Django的信号功能和Celery。不过现在我只是想先做一个概念验证,看看这个方法是否可行。
我想在上传新视频后执行一个video_replace()
的方法(也就是说,数据库里新增了一行数据)。但是信号没有正常工作,或者我没有理解整个系统是怎么运作的。
我使用的是Django 1.2.3,并且用到了预定义的信号django.db.models.signals.post_init
,这个信号应该在模型实例化后被调用(也就是数据库里新增了一行数据)。
from django.core.files.base import File
from django.db.models.signals import post_init
import os
import os.path
import subprocess
class Project(models.Model):
video = models.FileField(upload_to="projects/videos")
def replace_video(self):
"""Replace original video with an updated one."""
# Video conversion process code goes here,
# resulting in a new external video file.
self.video.delete() # Delete the original video.
self.video.save("newfile.webm", File(open("path/to/newfile.webm") ,"wb"))) # Save the new video instead.
self.save() # Commit everything to database.
os.remove("path/to/newfile.webm") # Remove original video copy after it was commited (copied) into the DB.
# ...
# ...
def handle_new_project(sender, **kwargs):
"""Handels some additional tasks for a new added project. i.e. convert video to uniform format."""
project = kwargs['instance']
project.replace_video()
# Call 'Project.replace_video()' every time a new project is added.
post_init.connect(handle_new_project, sender=Project, dispatch_uid="new_project_added")
然而,post_init不仅在创建新模型实例时被调用,还会在以下情况下被调用…:
- 在模型实例化之前。我的意思是,当我第一次启动服务器时,它会被调用,那时数据库里甚至没有一行数据(因此不应该实例化任何模型对象)。这个实例的
self.pk
是None
! - 在调用
save()
方法时。上面的代码在我执行self.save()
时也会被执行。
实际上,它的工作方式和文档上说的不太一样。
我哪里做错了?请记住,这只是一个概念验证。我打算在确认它有效后把代码移到Celery上。但是,如果信号不正常工作,Celery也帮不了忙——每次我save()
或更新视频时,信号都会被多次发送。
你觉得我不应该在replace_video()
方法里调用save()
吗?那我应该在哪里调用呢?我应该选择哪个信号?post_save
不是一个好选择,因为它在我点击save()
时也会被调用。
2 个回答
我知道这个问题已经很久了,但我遇到过类似的问题,解决起来很简单,只需要使用 post_save 信号,并检查一下是否是新创建的。
@receiver(post_save, sender=YourSender)
def when_init(sender, instance, created, **kwargs):
if created:
...
你似乎对“实例化一个对象”这个概念有点困惑。其实这和数据库没有任何关系。实例化一个模型对象时,并不会把它保存到数据库里,这种情况下它的主键(pk)会是None:
MyObject(field1='foo', field2='bar')
而这个(间接地)是通过从数据库获取对象来实例化一个对象:
MyObject.objects.get(field1='baz')
在这两种情况下,post_init
信号都会被发送,尽管它们都和保存到数据库没有关系。
如果你希望在保存的时候触发某些操作,可以选择重写save
方法,或者使用pre_save
或post_save
信号。你可以通过检查对象的pk
是否为None来判断它之前是否被保存过。