1

I've got a Django model like so...

class Example(models.Model):
    title = models.CharField(...)
    ...

I'm trying to compare two values - the title field before the user changes it, and the title after. I don't want to save both values in the database at one time (only need one title field), so I'd like to use pre_save and post_save methods to do this. Is it possible to get the title before the save, then hold this value to be passed into the post_save method?

The pre_save and post_save methods look like so...

@receiver(pre_save, sender=Example, uid='...')
def compare_title_changes(sender, instance, **kwargs):
    # get the current title name here
    x = instance.title

@receiver(post_save, sender=Example, uid='...')
def compare_title_changes(sender, instance, **kwargs):
    # get the new title name here and compare the difference
    x = instance.title # <- new title
    if x == old_title_name: # <- this is currently undefined, but should be retrieved from the pre_save method somehow
        ...do some logic here...

Any ideas would be greatly appreciated!

Edit

As was pointed out to me, pre_save and post_save both occur after save() is called. What I was looking for is something like pre_save() but before the actual save method is called. I set this on the model so that the logic to be performed will be accessible wherever the instance is saved from (either admin or from a user view)

jayt
  • 758
  • 1
  • 14
  • 32
  • 1
    You might be looking at the wrong direction. `pre_save` is called AFTER the user changes the value (somewhere in your code `example.save()`, such as [here](https://docs.djangoproject.com/en/2.0/ref/models/instances/#updating-attributes-based-on-existing-fields)) and BEFORE the data reach the database. – gdlmx Aug 04 '18 at 22:05
  • What is the reason that the logic in the post_save callback isn't done in the pre_save callback? – Oluwafemi Sule Aug 04 '18 at 22:05
  • @gdlmx I see, my mistake! Thanks very much. I'll edit my question to reflect this - how might I achieve this effect at the models.py level? I set it at the model level since I want the logic to be available whenever the model is saved (either in the admin, or a user view, etc.) – jayt Aug 04 '18 at 22:10

2 Answers2

5

Use Example.objects.get(pk=instance.id) to get the old title from the database in the pre_save handler function:

@receiver(pre_save, sender=Example, uid='...')
def compare_title_changes(sender, instance, **kwargs):

    new_title = instance.title  # this is the updated value
    old_title = Example.objects.get(pk=instance.id)
    # Compare the old and new titles here ...

This trick was proposed here a long time ago. I've not tested it with the recent Django version. Please let me know whether it's still working.

gdlmx
  • 6,479
  • 1
  • 21
  • 39
1

We can only say object has changed if "save" method passes successfully, so post_save is good to be sure that model object has updated.

Setting on the fly attribute on model class instance can do the task, as the same instance is passed from pre_save to post_save.

def set_flag_on_pre_save(sender, instance, *args, **kwargs):
    # Check here if flag setting condition satisfies
    set_the_flag = true
    if set_the_flag:  
      instance.my_flag=0


def check_flag_on_post_save(sender, instance, *args, **kwargs):
   try:
      print(instance.my_flag)
      print('Flag set') 
   except AttributeError:
      print('Flag not set')


pre_save.connect(set_flag_on_pre_save, sender=ModelClass)

post_save.connect(check_flag_on_post_save, sender=ModelClass)
Sachin G.
  • 1,870
  • 19
  • 24