Django: перед обновлением модели я хотел бы "посмотреть" на ее предыдущие атрибуты
Когда обновление/создание выполняется в модели Django (.save()
), я хотел бы иметь возможность "входить" и сравнивать некоторые конкретные атрибуты с тем, что они были установлены ранее (если они ранее существовали вообще),
Я думаю, что Pre-Save Signals, с исходной моделью, сделавшей .objects.get(instance.id)
, но это кажется расточительным. Кроме того, проверка уже произошла в pre_save()
?
Ответы
Ответ 1
проверка модели:
Обратите внимание, что full_clean() не будет вызываться автоматически, когда вы вызываете метод save() моделей
Затем, о pre-save signal
, обратите внимание, что вы получаете экземпляр, который сохраняется, отправленный в качестве параметра с сообщением. Поскольку прежняя версия вашей модели существует только в базе данных, я не вижу, где еще вы могли бы получить предыдущее значение атрибутов...
Вы не говорите, почему вы хотите сделать это, так что трудно сказать, но другие решения, о которых я сейчас думаю:
* defining a custom signal that is sent everytime the attributes you are interested in are modified... This signal would then send two arguments : new value, old value
* perform the check directly when setting the attributes
Если вы дадите более подробную информацию, это может быть проще...
ИЗМЕНИТЬ:
Правильно... Если вы выбрали пользовательский 'foo_has_updated', вы не будете уверены, что эта модификация будет сохранена.
В этом случае, я думаю, вы могли бы кэшировать переменные, которые вас интересуют при инициализации экземпляра, и улавливать сигнал предварительной записи после сохранения.
* With pre-save, you would be able to pre-process the data, but the saving operation might fail
* With post-save, you would be sure that the data has been saved.
Кэширование ваших переменных может быть выполнено следующим образом:
class CachedModel(models.Model):
cached_vars = [var1, var2, varN]
def __init__(self, *args, **kwargs):
super(CachedModel, self).__init__(*args, **kwargs)
self.var_cache = {}
for var in self.cached_vars:
self.var_cache[var] = copy.copy(getattr(self, var))
Или что-то вроде этого... Затем в вашем обработчике сигнала:
def post_save_handler(sender, **kwargs):
instance = kwargs["instance"]
[(instance.var_cache[var], getattr(instance, var)) for var in instance.cached_var]
#[(<initial value>, <saved value>)
И у вас есть то, что вам нужно (я думаю)!!!
Ответ 2
Здесь моя идея: играть со свойствами.
Скажем, у вас есть этот класс:
class Foo(models.Model):
name = models.CharField()
Вместо этого переименуйте свое поле (вам не потребуется перенос, если он первый раз
вы делаете это) и:
class Foo(models.Model):
_name = models.CharField()
@property
def name(self):
return self._name
@name.setter
def name(self, new_value):
if not getattr(self, '_initial_name', False):
self._initial_name = self._name
if new_value != self._initial_name:
self._name_changed = True
else:
self._name_changed = False
self._name = new_value
Мы добавили два атрибута в ваши экземпляры Foo: '_initial_name' и
'_name_changed' и свойство: 'name'. Это не поля модели, а
никогда не сохраняться в базе данных. Кроме того, вам не придется связываться с '_name'
поле дольше, пока свойство name будет заботиться обо всем.
Теперь ваш обработчик сигнала 'pre_save' или 'post_save' может проверять, что
изменено:
def handle_pre_save(sender, **kwargs):
foo = kwargs['instance']
if getattr(foo, '_name_changed', False):
log.debug("foo changed its name from '%s' to '%s'",
foo._initial_name, foo.name)
Ответ 3
Вы можете запросить сохраненные значения в базе данных перед сохранением. Я сделал это для регистрации только измененных значений, но при каждом сохранении он вызывает другой запрос базы данных.
Ответ 4
Хотя я очень одобряю ответ Sébastien Piquemal, я в конечном итоге закончил использование сигналов pre_save
и post_save
. Вместо того, чтобы переопределять __init__()
, я делаю что-то очень похожее в pre_save
, а затем проверяю/сравниваю значения в post_save
и издаю оттуда специальный сигнал, если выполняются определенные условия.
Я думаю, что я по-прежнему соглашусь с его ответом, потому что на это потрачено время. Я не вижу, где мы делаем много, кроме того, что я делаю свою работу в сигнале, и он делает это во время инициализации.
Ответ 5
Вы можете использовать сигнал pre_save и сравнить запись db (старая версия) с записью экземпляра (обновленная, но не сохраненная в версии db).
Возьмите модель, подобную этой, например:
class Person(models.Model):
Name = models.CharField(max_length=200)
В функции pre_save вы можете сравнить версию экземпляра
с версией db.
def check_person_before_saving(sender, **kwargs):
person_instance = kwargs['instance']
if person_instance.id:
# you are saving a Person that is already on the db
# now you can get the db old version of Person before the updating
# you should wrap this code on a try/except (just in case)
person_db = Person.objects.get(id=person_instance.id)
# do your compares between person_db and person_instance
...
# connect the signal to the function. You can use a decorator if you prefer
pre_save.connect(check_person_before_saving, sender=Person)