Francisco
Francisco

Reputation: 1382

Django - Check diference between old and new value when overriding save method

thanks for your time.

I'm on Django 1.4, and I have the following code: Its the overriden save method for my Quest model.

@commit_on_success
def save(self, *args, **kwargs):
    from ib.quest.models.quest_status_update import QuestStatusUpdate
    created = not self.pk

    if not created:
        quest = Quest.objects.get(pk=self)
        # CHECK FOR SOME OLD VALUE
    super(Quest, self).save(*args, **kwargs)

I couldn't find out a smart way of doing this. It seems very silly to me to have to make a new query for the object i'm currently updating in order to find out an old instance value.

Is there a better way to do this?

Thank you all.

Francisco

Upvotes: 17

Views: 11292

Answers (3)

Simon Steinberger
Simon Steinberger

Reputation: 6825

You can store the old value inside the init method:

def __init__(self, *args, **kwargs):
    super(MyModel, self).__init__(*args, **kwargs)
    self.old_my_field = self.my_field

def save(self, *args, **kwargs):
    print self.old_my_field
    print self.my_field

You can probably use deepcopy or something alike to copy the whole object for later use in the save and delete methods.

Upvotes: 21

jondykeman
jondykeman

Reputation: 6272

I am checking the difference to old values using a django-reversion signal, but the same logic would apply to the save signals. The difference for me being that I want to save whether the field was saved or not.

@receiver(reversion.pre_revision_commit)
def it_worked(sender, **kwargs):
    currentVersion = kwargs.pop('versions')[0].field_dict
    fieldList = currentVersion.keys()
    fieldList.remove('id')
    commentDict = {}
    print fieldList
    try:
        pastVersion = reversion.get_for_object(kwargs.pop('instances')[0])[0].field_dict
    except IndexError:
        for field in fieldList:
            commentDict[field] = "Created"
        comment = commentDict
    except TypeError:
        for field in fieldList:
            commentDict[field] = "Deleted"
        comment = commentDict
    else:
        for field in fieldList:
            try:
                pastTest = pastVersion[field]
            except KeyError:
                commentDict[field] = "Created"
            else:       
                if currentVersion[field] != pastTest:
                    commentDict[field] = "Changed"
                else:
                    commentDict[field] = "Unchanged"
        comment = commentDict
    revision = kwargs.pop('revision')
    revision.comment = comment
    revision.save()
    kwargs['revision'] = revision
    sender.save_revision

Upvotes: 1

Ian Clelland
Ian Clelland

Reputation: 44192

Django doesn't cache the old values of the model instance, so you need to do that yourself or perform another query before save.

One common pattern is to use a pre-save signal (or put this code directly in your save() method, as you've done):

old_instance = MyModel.objects.get(pk=instance.pk)
# compare instance with old_instance, and maybe decide whether to continue

If you want to keep a cache of the old values, then you would probably do that in your view code:

from copy import deepcopy
object = MyModel.objects.get(pk=some_value)
cache = deepcopy(object)

# Do something with object, and then compare with cache before saving

There was a recent discussion on django-developers about this as well, with some other possible solutions.

Upvotes: 12

Related Questions