from django.forms.models import model_to_dict


class ModelDiffMixin(object) :
    """ A model mixin that tracks model fields' values and provide some useful api to know what fields have been changed. "" "

    def __init__(self, *args, **kwargs) :
        super(ModelDiffMixin, self).__init__(*args, **kwargs)
        self.__initial = self._dict

    @property
    def diff(self) :
        d1 = self.__initial
        d2 = self._dict
        diffs = [(k, (v, d2[k])) for k, v in d1.items() ifv ! = d2[k]]return dict(diffs)

    @property
    def has_changed(self) :
        return bool(self.diff)

    @property
    def changed_fields(self) :
        return self.diff.keys()

    def get_field_diff(self, field_name) :
        """ Returns a diff for field if it's changed and None otherwise. """
        return self.diff.get(field_name, None)

    def save(self, *args, **kwargs) :
        """ Saves model and set initial state. """
        super(ModelDiffMixin, self).save(*args, **kwargs)
        self.__initial = self._dict

    @property
    def _dict(self) :
        return model_to_dict(self, fields=[field.name for field in
                             self._meta.fields])
Copy the code

Usage:

>>> p = Place()
>>> p.has_changed
False
>>> p.changed_fields
[]
>>> p.rank = 42
>>> p.has_changed
True
>>> p.changed_fields
['rank']
>>> p.diff
{'rank': (0.42)}
>>> p.categories = [1.3.5]
>>> p.diff
{'categories': (None[1.3.5]), 'rank': (0.42)}
>>> p.get_field_diff('categories')
(None[1.3.5])
>>> p.get_field_diff('rank')
(0.42) > > >Copy the code

Resources: stackoverflow.com/questions/1…