Подтвердить что ты не робот

Django - сравнение старого и нового значения поля перед сохранением

У меня есть модель django, и мне нужно сравнить старые и новые значения поля, прежде чем экономить.

Я попробовал наследование save() и pre_save. Это было вызвано правильно, но я не могу найти список фактических измененных полей и не могу сравнивать старые и новые значения. Есть выход? Мне нужно это для оптимизации предохраненных действий.

Спасибо!

4b9b3361

Ответ 1

Для этого существует очень простой способ django.

"Запомните" значения в модели init следующим образом:

def __init__(self, *args, **kwargs):
    super(MyClass, self).__init__(*args, **kwargs)
    self.initial_parametername = self.parametername
    ---
    self.initial_parameternameX = self.parameternameX

Пример реальной жизни:

В классе:

def __init__(self, *args, **kwargs):
    super(MyClass, self).__init__(*args, **kwargs)
    self.__important_fields = ['target_type', 'target_id', 'target_object', 'number', 'chain', 'expiration_date']
    for field in self.__important_fields:
        setattr(self, '__original_%s' % field, getattr(self, field))

def has_changed(self):
    for field in self.__important_fields:
        orig = '__original_%s' % field
        if getattr(self, orig) != getattr(self, field):
            return True
    return False

И затем в методе сохранения модели:

def save(self, force_insert=False, force_update=False, commit=True):
    # Prep the data
    obj = super(MyClassForm, self).save(commit=False)

    if obj.has_changed():

        # If we're down with commitment, save this shit
        if commit:
            obj.save(force_insert=True)

    return obj

Ответ 2

Лучше сделать это на уровне ModelForm.

Там вы получите все данные, которые вам нужны для сравнения в методе сохранения:

  • self.data​​strong > : фактические данные, переданные в форму.
  • self.cleaned_data​​strong > : данные очищены после проверки, содержит данные, которые могут быть сохранены в модели
  • self.changed_data​​strong > : список полей, которые изменились. Это будет пустым, если ничего не изменилось.

Если вы хотите сделать это на уровне модели, вы можете следовать методу, указанному в ответе Odif.

Ответ 3

Также вы можете использовать FieldTracker от django-model-utils для этого:

  • Просто добавьте поле трекера в вашу модель:

    tracker = FieldTracker()
    
  • Теперь в pre_save и post_save вы можете использовать:

    instance.tracker.previous('modelfield')     # get the previous value
    instance.tracker.has_changed('modelfield')  # just check if it is changed
    

Ответ 4

Вот приложение, которое дает вам доступ к предыдущему и текущему значению поля прямо перед сохранением модели: django-smartfields

Вот как эта проблема может быть решена в хорошем декларативном состоянии:

from django.db import models
from smartfields import fields, processors
from smartfields.dependencies import Dependency

class ConditionalProcessor(processors.BaseProcessor):

    def process(self, value, stashed_value=None, **kwargs):
        if value != stashed_value:
            # do any necessary modifications to new value
            value = ... 
        return value

class MyModel(models.Model):
    my_field = fields.CharField(max_length=10, dependencies=[
        Dependency(processor=ConditionalProcessor())
    ])

Кроме того, этот процессор будет вызываться только в том случае, если значение поля было заменено

Ответ 5

Моим вариантом использования для этого было то, что мне нужно было установить денормализованное значение в модели всякий раз, когда какое-либо поле изменило его значение. Тем не менее, поскольку контролируемое поле было отношением m2m, я не хотел, чтобы этот поиск БД выполнялся всякий раз, когда был вызван запрос, чтобы проверить, нужно ли обновлять денормализованное поле. Таким образом, вместо этого я написал этот маленький mixin (используя @Odif Yitsaeb ответ как вдохновение), чтобы обновлять денормализованное поле, когда это необходимо.

class HasChangedMixin(object):
    """ this mixin gives subclasses the ability to set fields for which they want to monitor if the field value changes """
    monitor_fields = []

    def __init__(self, *args, **kwargs):
        super(HasChangedMixin, self).__init__(*args, **kwargs)
        self.field_trackers = {}

    def __setattr__(self, key, value):
        super(HasChangedMixin, self).__setattr__(key, value)
        if key in self.monitor_fields and key not in self.field_trackers:
            self.field_trackers[key] = value

    def changed_fields(self):
        """
        :return: `list` of `str` the names of all monitor_fields which have changed
        """
        changed_fields = []
        for field, initial_field_val in self.field_trackers.items():
            if getattr(self, field) != initial_field_val:
                changed_fields.append(field)

        return changed_fields

Ответ 6

Я согласен с Sahil, что с ModelForm лучше и проще сделать это. Однако вы должны настроить метод CleanForm ModelForm и выполнить проверку там. В моем случае я хотел предотвратить обновление экземпляра модели, если задано поле на модели.

Мой код выглядел так:

from django.forms import ModelForm

class ExampleForm(ModelForm):
    def clean(self):
        cleaned_data = super(ExampleForm, self).clean()
        if self.instance.field:
            raise Exception
        return cleaned_data