Django accede a los campos ManyToMany desde la señal post_save

Tengo un modelo de Django y quiero modificar los permisos del objeto en o justo después de guardar. He intentado algunas soluciones y la señal post_save pareció la mejor candidata para lo que quiero hacer:

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

En este caso, cuando se crea un proyecto, la señal se dispara y veo el title , pero una lista vacía para el campo assigned_to .

¿Cómo puedo acceder a los datos assigned_to después de guardar?

No vas a hacerlo. Los M2M se guardan después de guardar las instancias y, por lo tanto, no habrá ningún registro en todas las actualizaciones m2m. Otros problemas (incluso si resuelve eso) son que todavía está en una transacción y consultar el DB no le permitirá obtener m2m con los estados correctos de todos modos.

La solución es enganchar en la señal m2m_changed lugar de post_save .

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

Su remitente sería Project.assigned_to.through

Si su m2m puede estar vacía (en blank=True ), tiene un pequeño problema con m2m_changed , porque m2m_changed no se activa si m2m no se configuró. Puedes resolver este problema usando post_save y m2m_changed al mismo tiempo. Pero hay una gran desventaja con este método: su código se ejecutará dos veces si el campo m2m no está vacío.

Entonces, puedes usar la transacción on_commit ( solo Django> = 1.9 )

Django proporciona la función on_commit () para registrar funciones de callback que se deben ejecutar después de que una transacción se haya confirmado correctamente.

 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