Django: Прежде чем обновить модель, я бы хотел посмотреть на ее предыдущие атрибуты - PullRequest
13 голосов
/ 10 июля 2010

Когда выполняется обновление / создание для модели Django (.save()), я хотел бы иметь возможность «вмешаться» и сравнить некоторые конкретные атрибуты с теми, к которым они были установлены ранее (если они ранее вообще существовали) ,

Я думаю, Pre-Save Signals , с поиском исходной модели, которая делает .objects.get(instance.id), но это кажется расточительным. Кроме того, проверка уже произошла в pre_save()?

Ответы [ 5 ]

7 голосов
/ 10 июля 2010

около подтверждение модели :

Обратите внимание, что 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 голосов
/ 09 июля 2012

Вот моя идея: поиграть со свойствами .

Допустим, у вас есть этот класс:

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» позаботится обо всем.

Теперь ваш обработчик сигналов «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)
0 голосов
/ 22 апреля 2015

Вы можете использовать сигнал pre_save и сравнить запись БД (старая версия) с записью экземпляра (обновленной, но не сохраненной в версии БД).

В качестве примера возьмем такую ​​модель:

class Person(models.Model):
    Name = models.CharField(max_length=200)

В функции pre_save вы можете сравнить версию экземпляра с версией БД.

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)
0 голосов
/ 25 августа 2010

Хотя я очень одобряю ответ Себастьена Пикемала В конечном итоге я использовал сигналы pre_save и post_save.Вместо переопределения __init__() я делаю что-то очень похожее в pre_save, а затем проверяю / сравниваю значения в post_save и выдаю собственный сигнал оттуда, если выполняются определенные условия.

Думаю, я все равно приму его ответ, потому что потратил на это время.Я не вижу, где мы делаем все по-другому, за исключением того, что я делаю свою работу в сигнале, а он делает это во время инициализации.

0 голосов
/ 11 июля 2010

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

...