Django 从 post_save 信号访问 ManyToMany 字段

Posted

技术标签:

【中文标题】Django 从 post_save 信号访问 ManyToMany 字段【英文标题】:Django accessing ManyToMany fields from post_save signal 【发布时间】:2014-07-10 20:10:44 【问题描述】:

我有一个 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 数据?

【问题讨论】:

您应该使用Concern 作为sender 对象,将模型名称更改为Project 而不是Concern 糟糕 - 错字。 Concern 实际上是一个抽象基类。现已编辑。 【参考方案1】:

如果您的 m2m 可以为空 (blank=True),那么您在使用 m2m_changed 时会遇到一些麻烦,因为如果未设置 m2m,m2m_changed 就不会触发。您可以同时使用post_savem2m_changed 来解决此问题。但是这种方法有一个很大的缺点 - 如果 m2m 字段不为空,您的代码将被执行两次。

所以,你可以使用事务的on_commit (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_ultimate_func(sender, **kwargs):
    # Do things here

重要提示:此方法仅在您的代码调用 save() 时有效。 当您只调用instance.m2m.add()instance.m2m.set() 时,post_save 信号根本不会触发。

【讨论】:

我尝试使用 on_commit 和 post save(你写的最后一个代码),m2m 模型中的更改仍然没有应用。 @Daniel 可能您的代码根本没有调用save(),这就是post_save 信号不起作用的原因。【参考方案2】:

你不会的。保存实例后保存 M2M,因此所有 m2m 更新都不会有任何记录。进一步的问题(即使你解决了这个问题)是你仍然处于事务中并且查询数据库不会让你得到正确状态的 m2m。

解决方案是挂钩m2m_changed 信号而不是post_save

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

您的发件人将是Project.assigned_to.through

【讨论】:

我的 ForeignKey 也有同样的问题,但显然没有信号? +1 用于识别发件人(many-to-many_field.through)。如果没有使用显式模型定义 m2m 关系,它会很方便。 似乎m2m_changed 没有在管理员中被解雇。那是对的吗?也许相关:code.djangoproject.com/ticket/16073

以上是关于Django 从 post_save 信号访问 ManyToMany 字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Django 信号(Pre_save,Post_save)从“B”模型的 ImageField 设置“A”模型的 ImageField

识别 django post_save 信号中更改的字段

Django post_save() 信号实现

如何防止灯具与 django post_save 信号代码冲突?

忽略 django 的 post_save 信号中对 m2m 关系的更改

从 django post save 信号产生一个线程可以吗?