Django accede a ManyToMany Field dal segnale post_save

Ho un modello Django e voglio modificare le autorizzazioni dell’object su o subito dopo il salvataggio. Ho provato alcune soluzioni e il segnale post_save è sembrato il miglior candidato per quello che voglio fare:

  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 questo caso, quando viene creato un progetto, il segnale si triggers e vedo il title , ma una lista vuota per il campo assigned_to .

Come posso accedere ai dati assigned_to seguito del salvataggio?

Non lo farai. I M2M vengono salvati dopo che le istanze sono state salvate e quindi non ci sarà alcun record in tutti gli aggiornamenti di m2m. Ulteriori problemi (anche se lo risolvete) sono che siete ancora in una transazione e interrogando il DB non otterrete comunque m2m con gli stati appropriati.

La soluzione è agganciare il segnale m2m_changed invece di post_save .

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

Il tuo mittente sarà quindi Project.assigned_to.through

Se il m2m può essere vuoto ( blank=True ) si ha un piccolo problema con m2m_changed , perché m2m_changed non si m2m_changed se m2m non è stato impostato. Puoi risolvere questo problema usando post_save e m2m_changed allo stesso tempo. Ma c’è un grosso svantaggio con questo metodo: il codice verrà eseguito due volte se il campo m2m non è vuoto.

Quindi, puoi usare on_commit della transazione ( solo Django> = 1.9 )

Django fornisce la funzione on_commit () per registrare le funzioni di callback che dovrebbero essere eseguite dopo che una transazione è stata eseguita correttamente.

 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