Django - сравнение старого и нового значения поля перед сохранением
У меня есть модель django, и мне нужно сравнить старые и новые значения поля, прежде чем экономить.
Я попробовал наследование save() и pre_save. Это было вызвано правильно, но я не могу найти список фактических измененных полей и не могу сравнивать старые и новые значения. Есть выход? Мне нужно это для оптимизации предохраненных действий.
Спасибо!
Ответы
Ответ 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.datastrong > : фактические данные, переданные в форму.
- self.cleaned_datastrong > : данные очищены после проверки, содержит данные, которые могут быть сохранены в модели
- self.changed_datastrong > : список полей, которые изменились. Это будет пустым, если ничего не изменилось.
Если вы хотите сделать это на уровне модели, вы можете следовать методу, указанному в ответе 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