Django: Before a model is updated, I'd like to "look at" its previous attributes

anonymous coward picture anonymous coward · Jul 9, 2010 · Viewed 7.3k times · Source

When an update/create is performed on a Django model (.save()) I would like to be able to "step in" and compare some particular attributes to what they were set to previously (if they previously existed at all).

I'm thinking Pre-Save Signals, with a look-up to the original model doing a .objects.get(instance.id), but that feels wasteful. Also, has the validation already happened in pre_save()?

Answer

sebpiq picture sebpiq · Jul 9, 2010

about model validation :

Note that full_clean() will not be called automatically when you call your model’s save() method

Then, about the pre-save signal, note that you get the instance that is being saved sent as a parameter with the message. As the former version of your model exists only in the database, I don't see where else you could get the previous value of the attributes ...

You don't tell why you want to do this so it's hard to say, but other solutions I'm thinking of right now :

* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes

If you give more details, it might be easier...

EDIT :

That's right ... If you emit a custom 'foo_has_updated', you will not be sure that the modification is saved.

In this case, I guess you could cache the variables that interest you while initializing the instance, and catch the post-save OR pre-save signal.

* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.

Caching your variables could be done like this :

class CachedModel(models.Model):
    cached_vars = [var1, var2, varN]
    def __init__(self, *args, **kwargs):
        super(CachedModel, self).__init__(*args, **kwargs)
        self.var_cache = {}
        for var in self.cached_vars:
            self.var_cache[var] = copy.copy(getattr(self, var))

Or something like this ... Then, in your signal handler :

def post_save_handler(sender, **kwargs):
    instance = kwargs["instance"]
    [(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
    #[(<initial value>, <saved value>)

And you got what you needed (I think)!!!