Django - 在模型实例保存和调用上调用 post_init 信号在创建实例之前。为什么?

发布于 2024-10-12 07:42:52 字数 2361 浏览 1 评论 0原文

我正在尝试编写一个接收视频文件的小应用程序,并在上传视频文件后将它们转换为统一格式(从而添加到数据库中)。我在网上搜索了最佳解决方案,并决定将 Django 信号与 Celery 结合使用。但现在我正在尝试创建一个概念验证,看看它是否有效。

我试图在上传新视频后执行 video_repalce() 方法(因此,新行已添加到数据库中)。但信号不正常,或者我不明白整个系统是如何工作的。

我使用 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 不仅在创建新模型实例时被调用,而且还在......:

  1. 甚至在模型实例化之前被调用。我的意思是,当我第一次执行服务器时,当数据库中甚至没有一行数据时(因此,不应实例化任何 Model 对象),就会调用它。实例的 self.pkNone
  2. save()-ing模型时。当我点击 self.save() 时,上面的代码也会执行。

实际上,它按照文档不起作用。

我做错了什么?请记住,这是一个概念验证。我打算在看到代码运行后将其移至 Celery 。但是,如果信号不能正常工作,Celery 将无济于事 - 信号总是会重新发送几次每当我 save() 或更新视频时。

您认为我不应该在 replace_video() 方法中调用 save() 吗?那么我应该在哪里调用它呢?我应该选择哪个信号? post_save 不是一个好的选择,因为每当我点击 save() 时它也会被调用。

I am trying to write a small application that receives video files, and convert them to a uniform format after they have been uploaded (thus added to the database). I have searched the web for the best solution for this, and have decided to use Django's signals with Celery. But for now I'm trying to create a proof-of-concept to see if it works.

I'm trying to execute a video_repalce() method after a new video has been uploaded (thus, a new row has been added to database). But the signal doesn't work right, or I didn't understand how the whole system works.

I'm using Django 1.2.3 with pre-defined signal django.db.models.signals.post_init, which should be called after a model has been instantiated (thus, a new row was added to the database).

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")

However, post_init is called not only when a new model instance is created, but also...:

  1. Before the model is even instantiated. What I mean is, it's called when I exeucte the server for the first time, when there's not even a single row of data in the database (thus, no Model objects should be instantiated). The instance's self.pk is None!
  2. When save()-ing a model. The code above is also executed when I hit self.save().

Practically, it not working according to the docs.

What am I doing wrong? Remember that this is a proof-of-concept. I intend to move the code to Celery after I see it's working. But, if the signals don't work right, Celery won't help - The signal will always be resent a couple of times whenever I save() or update a video.

Do you think I shouldn't call save() inside the replace_video() method? So where should I call it? Which signal should I choose? post_save isn't a good option because it's also called whenever I hit save().

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拔了角的鹿 2024-10-19 07:42:52

您似乎对实例化对象的含义有些困惑。它与数据库无关。这会实例化一个模型对象,而不将其保存到数据库,在这种情况下,它的 pk 将为 None:

MyObject(field1='foo', field2='bar')

并且这(间接)通过从数据库获取对象来实例化一个对象:

MyObject.objects.get(field1='baz')

post_init 信号将被发送到这两种情况,即使它们都与保存到数据库没有任何关系。

如果您希望在保存时发生某些事情,请重写 save 方法本身,或使用 pre_savepost_save 信号。您可以通过验证其 pk 是否为 None 来检查该对象之前是否已保存。

You seem to have a bit of confusion over what it means to instantiate an object. It has nothing whatever to do with the database. This instantiates a model object without saving it to the database, in which case its pk will be None:

MyObject(field1='foo', field2='bar')

and this (indirectly) instantiates an object by getting it from the database:

MyObject.objects.get(field1='baz')

The post_init signal will be sent in both of these cases, even though neither of them has anything to do with saving to the database.

If you want something to happen when you save, either override the save method itself, or use the pre_save or post_save signals. You can check there whether or not the object has been previously saved, by verifying if its pk is None.

掐死时间 2024-10-19 07:42:52

我知道这个问题很旧,但我有一个类似的问题和简单的解决方案,只需使用 post_save 信号并检查是否已创建

@receiver(post_save, sender=YourSender) 
   def when_init(sender, instance, created, **kwargs):
      if created:
         ...

I know the question is old but I had a similar problem and easy solution for that is just using post_save signal and check if created

@receiver(post_save, sender=YourSender) 
   def when_init(sender, instance, created, **kwargs):
      if created:
         ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文