Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - post_init signal is called on Model instance save & before instance is even created. Why?

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().

like image 565
Ory Band Avatar asked Jan 15 '11 14:01

Ory Band


1 Answers

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.

like image 66
Daniel Roseman Avatar answered Sep 29 '22 14:09

Daniel Roseman