When saving, how can you check if a field has changed?

前端 未结 25 1794
鱼传尺愫
鱼传尺愫 2020-11-22 07:15

In my model I have :

class Alias(MyBaseModel):
    remote_image = models.URLField(max_length=500, null=True, help_text=\"A URL that is downloaded and cached          


        
25条回答
  •  渐次进展
    2020-11-22 07:36

    I use following mixin:

    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() if v != 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])
    

    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)
    >>>
    

    Note

    Please note that this solution works well in context of current request only. Thus it's suitable primarily for simple cases. In concurrent environment where multiple requests can manipulate the same model instance at the same time, you definitely need a different approach.

提交回复
热议问题