35

I see I can override or define pre_save, save, post_save to do what I want when a model instance gets saved.

Which one is preferred in which situation and why?

karthikr
  • 97,368
  • 26
  • 197
  • 188
eugene
  • 39,839
  • 68
  • 255
  • 489

3 Answers3

41

I shall try my best to explain it with an example:

pre_save and post_save are signals that are sent by the model. In simpler words, actions to take before or after the model's save is called.

A save triggers the following steps

  • Emit a pre-save signal.
  • Pre-process the data.
  • Most fields do no pre-processing — the field data is kept as-is.
  • Prepare the data for the database.
  • Insert the data into the database.
  • Emit a post-save signal.

Django does provide a way to override these signals.

Now,

pre_save signal can be overridden for some processing before the actual save into the database happens - Example: (I dont know a good example of where pre_save would be ideal at the top of my head)

Lets say you have a ModelA which stores reference to all the objects of ModelB which have not been edited yet. For this, you can register a pre_save signal to notify ModelA right before ModelB's save method gets called (Nothing stops you from registering a post_save signal here too).

Now, save method (it is not a signal) of the model is called - By default, every model has a save method, but you can override it:

class ModelB(models.Model):
    def save(self):
        #do some custom processing here: Example: convert Image resolution to a normalized value
        super(ModelB, self).save()

Then, you can register the post_save signal (This is more used that pre_save)

A common usecase is UserProfile object creation when User object is created in the system.

You can register a post_save signal which creates a UserProfile object that corresponds to every User in the system.

Signals are a way to keep things modular, and explicit. (Explicitly notify ModelA if i save or change something in ModelB )

I shall think of more concrete realworld examples in an attempt to answer this question better. In the meanwhile, I hope this helps you

karthikr
  • 97,368
  • 26
  • 197
  • 188
  • Thanks for the detailed answer. The reason why `post_save` is used for `UserProfile` creation is because you can check whether the instance is `created` or not in post_save?(but not in pre_save, save) – eugene Jul 15 '13 at 15:57
  • You might want to add to the answer that pre_save or post_save have per se nothing to do with transactions. If you use get_or_create, or request level transactions, post_save is going to happen *inside* the transaction, not after it. Depending on the database you can use some of the post_commit signal implementations out there. – ashwoods Apr 21 '16 at 10:17
5
pre_save

it's used before the transaction saves.

post_save

it's used after the transaction saves.

You can use pre_save for example if you have a FileField or an ImageField and see if the file or the image really exists.

You can use post_save when you have an UserProfile and you want to create a new one at the moment a new User it's created.

Victor Castillo Torres
  • 10,581
  • 7
  • 40
  • 50
  • for your `pre_save` example where you gave an example to check if file exists, which one is better to use signal or override the save method?? because I had problem to raise error to my form if I add validation in my `pre_save` signal. – Adiyat Mubarak Apr 13 '15 at 12:01
  • 1
    @Keda87 If u r using a form I would go for overriding the validation function of the form. – Victor Castillo Torres Apr 13 '15 at 20:32
  • 2
    nope! post_save is normally called in an atomic block. That means post_save happens *inside* the transaction. – ashwoods Mar 10 '16 at 18:04
5

Don't forget about recursions risk. If you use post_save method with instance.save() calling, instead of .update method, you should disconnect your post_save signal:

Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)[source] To disconnect a receiver from a signal, call Signal.disconnect(). The arguments are as described in Signal.connect(). The method returns True if a receiver was disconnected and False if not.

The receiver argument indicates the registered receiver to disconnect. It may be None if dispatch_uid is used to identify the receiver.

... and connect it again after.

update() method don't send pre_ and post_ signals, keep it in mind.

SlowSuperman
  • 488
  • 1
  • 8
  • 14