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