4

I want to run some code after my model is saved. Three places to do this will be

1) override save method:

def save(self, *args, **kwargs):
        super(Blog, self).save(*args, **kwargs) 
        do_something()

2) use post_save signal:

def model_created(sender, **kwargs)
    the_instance = kwargs['instance']
    if kwargs['created']:
        do_something()

post_save.connect(model_created, sender=YourModel)

3) do this in the view itself.

if form.is_valid():
   do_something()
   return redirect(reverse("homepage"))

All three should work. This post advocates the second one.

My question is that the second method (post_save) works when the model is first created. I want to call do_something() whenever a new model is created and also when an existing model is updated. In the latter case, I will have kwargs[created] to be false. how do I handle this situation?

P.S: I am currently doing the third one which works fine for both updateView and CreateView. but the problem is it takes time to return from the do_something() function before it is being redirected.

Community
  • 1
  • 1
brain storm
  • 30,124
  • 69
  • 225
  • 393
  • Why don't omit `if kwargs['created']` check in the `post_save` signal handler? – alecxe May 07 '14 at 19:07
  • I want it to handle when both model is created or when it is updated. without the check, the first is not recommended – brain storm May 07 '14 at 19:36
  • Remove the `if kwargs['created']` check and `do_something()` would be called whenever model is created or updated. – alecxe May 07 '14 at 19:57
  • yes, I understand what you mean. but I guess it is a not a good practice to call something before checking it is created.. – brain storm May 07 '14 at 20:03
  • *the problem is it takes time to return from the do_something() function before it is being redirected* The exact same time will be taken in all three cases. Signals in Django are not asynchronous. – Kevin Christopher Henry May 07 '14 at 22:38
  • @KevinChristopherHenry: yes, you are correct. As pointed out, I should use celery or some multiprocessing module? – brain storm May 08 '14 at 18:12
  • 1
    That's probably the way to go. There are packages that allow you to use the signals API while specifying that something should execute asynchronously on celery (for example [async-signals](https://github.com/nyergler/async-signals), though I haven't used it personally). – Kevin Christopher Henry May 08 '14 at 18:48

1 Answers1

4

post_save signal is the cleanest option among these three.

Just eliminate if kwargs['created'] and call do_something() right in the body of a function:

def model_created_or_updated(sender, **kwargs):
    the_instance = kwargs['instance']
    do_something()

post_save.connect(model_created_or_updated, sender=YourModel)

Now, every time YourModel is created or updated do_something() would be called.

FYI, created is just a flag that indicates whether YourModel was created (value is True) or updated (value is False).

Note that do_something() will be executed synchronously and will be "blocking" until done. If you want to execute do_something asynchronously, you need to go with a real task management tool, like celery package.

alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195