Идентифицировать измененные поля в django post_save signal

19

Я использую django post_save для выполнения некоторых инструкций после сохранения модели.

class Mode(models.Model):
    name = models.CharField(max_length=5)
    mode = models.BooleanField()


from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
        # do some stuff
        pass

Теперь я хочу выполнить оператор в зависимости от того, изменилось ли значение поля mode или нет.

@receiver(post_save, sender=Mode)
def post_save(sender, instance, created, **kwargs):
        # if value of 'mode' has changed:
        #  then do this
        # else:
        #  do that
        pass

Я просмотрел несколько потоков SOF и блог, но не смог найти решение. Все они пытались использовать метод или форму pre_save, которые не являются моим прецедентом. Ссылка в документах django не упоминает прямой способ сделать это.

Ответ в приведенной ниже ссылке выглядит многообещающим, но я не знаю, как его использовать. Я не уверен, поддерживает ли последняя версия django или нет, потому что я использовал ipdb для отладки этого и обнаружил, что переменная instance не имеет атрибута has_changed , как указано в нижеприведенном ответе.

Django : При сохранении, как вы можете проверить, изменилось ли поле?

    
задан Wendy 19.04.2016 в 15:14
источник

2 ответа

13

Настройте его на __init__ вашей модели, чтобы у вас был доступ к ней.

def __init__(self, *args, **kwargs):
    super(YourModel, self).__init__(*args, **kwargs)
    self.__original_mode = self.mode

Теперь вы можете выполнить что-то вроде:

if instance.mode != instance.__original_mode:
    # do something useful
    
ответ дан scoopseven 19.04.2016 в 16:53
  • Осторожно. Этот подход имеет свои недостатки, о чем здесь подробно рассказывается. В двух словах: это не гонка, и может быть и неправильной, если у вас есть два экземпляра python, указывающие на одну и ту же строку db. –  Arnaud P 26.04.2017 в 19:04
18

Обычно лучше переопределить метод сохранения, чем использовать сигналы.

  

Из Два ложки django :   «Использовать сигналы в качестве крайней меры».

Я согласен с ответом @scoopseven о кэшировании исходного значения в init, но переопределяя метод сохранения, если это возможно.

class Mode(models.Model):
    name = models.CharField(max_length=5)
    mode = models.BooleanField()
    __original_mode = None

    def __init__(self, *args, **kwargs):
        super(Mode, self).__init__(*args, **kwargs)
        self.__original_mode = self.mode

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        if self.mode != self.__original_mode:
            #  then do this
        else:
            #  do that

        super(Mode, self).save(force_insert, force_update, *args, **kwargs)
        self.__original_mode = self.mode
    
ответ дан educolo 17.05.2016 в 02:17
  • Что случилось с использованием сигналов в Django? Это помогает вам создать рабочий процесс красиво. –  Hussain 28.09.2016 в 12:39
  • @Hussain, в то время как методы привязаны непосредственно к модели, и вы можете видеть ее поведение в одном месте, сигналы могут быть размещены в нескольких разных приложениях и сделать отладку кошмаром и кодом менее читабельными. Значит, речь идет не о сигналах, а о методах, которые более очевидны, и вам лучше придерживаться их, если сможете. –  r_black 29.09.2016 в 11:11
  • __original_mode не является потокобезопасным ...... –  Elwin Arens 05.09.2017 в 10:15