web-dev-qa-db-ja.com

Django post_save signalで変更されたフィールドを特定する

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メソッドまたはフォームを使用しようとしました。 https://docs.djangoproject.com/es/1.9/ref/signals/#post-save in Django docsは直接的な方法について言及していないこれを行う。

以下のリンクの回答は有望に見えますが、使用方法がわかりません。最新のDjango=バージョンがこれをサポートしているかどうかはわかりません。これはipdbを使用してこれをデバッグし、instance変数に属性has_changed以下の回答に記載されているとおり。

Django:保存時に、フィールドが変更されたかどうかをどのように確認できますか?

34
Wendy

__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
17
scoopseven

通常、信号を使用するよりもsaveメソッドをオーバーライドする方が適切です。

From Djangoの2つのスクープ :「最後の手段として信号を使用します。」

Initの元の値をキャッシュすることについて@scoopsevenの回答に同意しますが、可能であればsaveメソッドをオーバーライドします。

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
27
educolo

これは古い質問ですが、最近この状況に遭遇しました。次のことを実行して達成しました。

class Mode(models.Model):

    def save(self, *args, **kwargs):
        if self.pk:
            # If self.pk is not None then it's an update.
            cls = self.__class__
            old = cls.objects.get(pk=self.pk)
            # This will get the current model state since super().save() isn't called yet.
            new = self  # This gets the newly instantiated Mode object with the new values.
            changed_fields = []
            for field in cls._meta.get_fields():
                field_name = field.name
                try:
                    if getattr(old, field_name) != getattr(new, field_name):
                        changed_fields.append(field_name)
                except Exception as ex:  # Catch field does not exist exception
                    pass
            kwargs['update_fields'] = changed_fields
        super().save(*args, **kwargs)

これは、アプリおよびDjango-adminからのすべての更新/保存をキャッチするため、より効果的です。

7
kingraphaii

保存アクションの前後で状態を比較したい場合は、pre_saveデータベース更新後になるはずのインスタンスを提供するシグナル。pre_saveでは、データベース内のインスタンスの現在の状態を読み取り、差異に基づいていくつかのアクションを実行できます。

from Django.db.models.signals import post_save, pre_save
from Django.dispatch import receiver


@receiver(pre_save, sender=MyModel)
def on_cahnge(sender, instance: MyModel, **kwargs):
    if instance.id is None: # new object will be created
        pass # write your code hier
    else:
        previous = MyModel.objects.get(id=instance.id)
        if previous.field_a != instance.field_a: # fielad will be updated
            pass  # write your code hier