web-dev-qa-db-ja.com

Django post_save信号からManyToManyフィールドにアクセスする

Django=モデルがあり、保存時または保存直後にオブジェクトの権限を変更したい。いくつかの解決策を試してみたところ、post_save信号が目的の最適な候補であると思われたすること:

    class Project(models.Model):
        title = models.CharField(max_length=755, default='default')
        assigned_to = models.ManyToManyField(
            User, default=None, blank=True, null=True
        )
        created_by = models.ForeignKey(
            User,
            related_name="%(app_label)s_%(class)s_related"
        )


    @receiver(post_save, sender=Project)
    def assign_project_perms(sender, instance, **kwargs):
        print("instance title: "+str(instance.title))
        print("instance assigned_to: "+str(instance.assigned_to.all()))

この場合、プロジェクトが作成されると信号が発生し、titleが表示されますが、assigned_toフィールドには空のリストが表示されます。

保存後に保存したassigned_toデータにアクセスするにはどうすればよいですか?

29
Darwin Tech

あなたは行くつもりはありません。 M2Mはインスタンスの保存後に保存されるため、すべてのm2m更新でレコードはありません。さらなる問題(それを解決したとしても)は、まだトランザクション内にあり、DBにクエリを実行しても、適切な状態のm2mが取得されないことです。

解決策は、m2m_changedではなくpost_save信号にフックすることです。

https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed

送信者はProject.assigned_to.throughになります

37

M2mが空の場合(blank=True)、m [m]が設定されていないとm2m_changedが起動しないため、m2m_changedで少し問題があります。この問題を解決するには、post_savem2m_changedを同時に使用します。ただし、この方法には大きな欠点が1つあります。m2mフィールドが空でない場合、コードが2回実行されます。

したがって、トランザクションの on_commitonly Django> = 1.9)を使用できます

Djangoは、トランザクションが正常にコミットされた後に実行する必要があるコールバック関数を登録するon_commit()関数を提供しています。

from Django.db import transaction

def on_transaction_commit(func):
    def inner(*args, **kwargs):
        transaction.on_commit(lambda: func(*args, **kwargs))

    return inner

@receiver(post_save, sender=SomeModel)
@on_transaction_commit
def my_untimate_func(sender, **kwargs):
    # Do things here
8
Mark Mishyn