Django Model : не удается инстанцировать пользовательское поле в сигнале

У меня следующая модель:

class SrdRevisionWording(ModelDiff):
    srd_revision = models.ForeignKey(SrdRevision, on_delete=models.CASCADE)
    word = models.CharField(max_length=50, null=False)
    designation = models.CharField(max_length=200, null=False)
    is_list = models.BooleanField(null=False, default=False)
    parent_element = models.IntegerField(null=True) # ID of the Wording element in the previous SRD Revision
    __original_instance = None
    with_signal = True

    def get_revision_history_message(self, created=False, deleted=False, updated_field=None, check_history=False):
        if created:
            return 'Word "{}" added'.format(self.word)

        if deleted:
            return 'Word "{}" deleted'.format(self.word)

        match updated_field:
            case 'word':
                return 'Word {} changed to {}'.format(self.__original_instance.word, self.word)
            case 'designation' | 'is_list':
                return 'Word {} modified'.format(self.word)

Атрибут __original_instance является пользовательским полем модели (отсутствует в базе данных). Этот атрибут инстанцируется во время сигнала pre_save :

@receiver(pre_save, sender=SrdRevisionDocument)
@receiver(pre_save, sender=SrdRevisionWording)
def model_pre_save_handler(sender, instance, **kwargs):
    print('pre_save')
    if instance.srd_revision.revision != 'A':
        try:
            previous_srd_revision_state = sender.objects.get(pk=instance.parent_element)
        except sender.DoesNotExist:
            instance.__original_instance = None
        else:
            instance.__original_instance = previous_srd_revision_state
        print(instance.__original_instance)

Сигнал print(instance.__original_instance) хорошо показывает мне объект SrdRevisionWording object (21). Я также могу напечатать объект в сигнале post_save :

@receiver(post_save, sender=SrdRevisionDocument)
@receiver(post_save, sender=SrdRevisionWording)
def handle_revision_history_element_changed(sender, instance, **kwargs):
    print('signal')
    print(instance.__original_instance)
    if instance.with_signal is not False:
       message = instance.get_revision_history_message(created=True, check_history=True)

Проблема возникает в последней строке при вызове instance.get_revision_history_message(). Я получил следующую ошибку в файле models.py:

return 'Word {} changed to {}'.format(self.__original_instance.word, self.word)
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'word'

Я думаю,

что это больше от моего непонимания ООП. Но может ли кто-нибудь объяснить мне, почему __original_instance кажется не инстанцированным/доступным для метода Model здесь?

Потому что атрибут __original_instance равен None при вызове get_revision_history_message в обработчике сигнала post_save. Это происходит потому, что сигнал post_save не сохраняет состояние __original_instance, заданное в сигнале pre_save. Чтобы решить эту проблему, вы можете использовать свойство instance._state.adding в Django, чтобы определить, создается ли экземпляр, и обработать логику соответствующим образом.

Например: Вы можете использовать такой вариант.....

class SrdRevisionWording(ModelDiff):
srd_revision = models.ForeignKey(SrdRevision, on_delete=models.CASCADE)
word = models.CharField(max_length=50, null=False)
designation = models.CharField(max_length=200, null=False)
is_list = models.BooleanField(null=False, default=False)
parent_element = models.IntegerField(null=True)  # ID of the Wording element in the previous SRD Revision
__original_instance = None
with_signal = True

def set_original_instance(self):
    if self.srd_revision.revision != 'A':
        try:
            self.__original_instance = SrdRevisionWording.objects.get(pk=self.parent_element)
        except SrdRevisionWording.DoesNotExist:
            self.__original_instance = None

Этот подход будет работать на поддержание состояния.

Вернуться на верх