Django从post_save信号访问ManyToMany字段

时间:2014-05-22 00:08:41

标签: django django-models django-signals

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

2 个答案:

答案 0 :(得分:36)

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

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

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

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

答案 1 :(得分:6)

如果您的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_untimate_func(sender, **kwargs):
    # Do things here