Django accessing ManyToMany fields from post_save signal

二次信任 提交于 2019-12-17 06:08:10

问题


I have a Django model and I want to modify the object permissions on or just after save. I have tried a few solutions and the post_save signal seemed the best candidate for what I want to do:

    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()))

In this case, when a Project is created, the signal fires and I see the title, but an empty list for the assigned_to field.

How can I access the saved assigned_to data following save?


回答1:


You're not going to. M2Ms are saved after instances are saved and thus there won't be any record at all of the m2m updates. Further issues (even if you solve that) are that you're still in a transaction and querying the DB won't get you m2m with proper states anyways.

The solution is to hook into the m2m_changed signal instead of post_save.

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

Your sender then would be Project.assigned_to.through




回答2:


If your m2m can be empty (blank=True) you are in a little trouble with m2m_changed, because m2m_changed doesn't fire if m2m wasn't set. You can solve this issue by using post_save and m2m_changed at the same time. But there is one big disadvantage with this method - your code will be executed twice if m2m field isn't empty.

So, you can use transaction's on_commit (only Django >= 1.9)

Django provides the on_commit() function to register callback functions that should be executed after a transaction is successfully committed.

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


来源:https://stackoverflow.com/questions/23795811/django-accessing-manytomany-fields-from-post-save-signal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!