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

前端 未结 25 2166
鱼传尺愫
鱼传尺愫 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:41

    I have extended the mixin of @livskiy as follows:

    class ModelDiffMixin(models.Model):
        """
        A model mixin that tracks model fields' values and provide some useful api
        to know what fields have been changed.
        """
        _dict = DictField(editable=False)
        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.
            """
            object_dict = model_to_dict(self,
                   fields=[field.name for field in self._meta.fields])
            for field in object_dict:
                # for FileFields
                if issubclass(object_dict[field].__class__, FieldFile):
                    try:
                        object_dict[field] = object_dict[field].path
                    except :
                        object_dict[field] = object_dict[field].name
    
                # TODO: add other non-serializable field types
            self._dict = object_dict
            super(ModelDiffMixin, self).save(*args, **kwargs)
    
        class Meta:
            abstract = True
    

    and the DictField is:

    class DictField(models.TextField):
        __metaclass__ = models.SubfieldBase
        description = "Stores a python dict"
    
        def __init__(self, *args, **kwargs):
            super(DictField, self).__init__(*args, **kwargs)
    
        def to_python(self, value):
            if not value:
                value = {}
    
            if isinstance(value, dict):
                return value
    
            return json.loads(value)
    
        def get_prep_value(self, value):
            if value is None:
                return value
            return json.dumps(value)
    
        def value_to_string(self, obj):
            value = self._get_val_from_obj(obj)
            return self.get_db_prep_value(value)
    

    it can be used by extending it in your models a _dict field will be added when you sync/migrate and that field will store the state of your objects

提交回复
热议问题