2010-07-14 5 views
5

Ecco la situazione:django: ricorsione con segnale post-salvataggio

Diciamo che ho un modello A in django. Quando sto salvando un oggetto (di classe A) ho bisogno di salvare i suoi campi in tutti gli altri oggetti di questa classe. Voglio dire, ho bisogno che ogni altro oggetto A sia copia di quello salvato da lat.

Quando uso segnali (post-save per esempio) ottengo una ricorsione (gli oggetti cercano di salvarsi l'un l'altro credo) e il mio pitone muore.

I uomini mi aspettavo che l'utilizzo del metodo .save() sulla stessa classe nel segnale di pre/post-salvataggio causasse una ricorsione, ma semplicemente non so come evitarlo.

Cosa facciamo?

+0

Forse aggiungi del codice in modo che la tua situazione diventi più chiara? – adamk

+0

Non succederà se usi pre_save, dato che non devi chiamare save() te stesso, allora succederà «naturalmente». –

risposta

4

Ciò funzionerà:

class YourModel(models.Model): 
    name = models.CharField(max_length=50) 

    def save_dupe(self): 
     super(YourModel, self).save() 

    def save(self, *args, **kwargs): 
     super(YourModel, self).save(*args, **kwargs) 
     for model in YourModel.objects.exclude(pk=self.pk): 
      model.name = self.name 
      # Repeat the above for all your other fields 
      model.save_dupe() 

Se si dispone di un sacco di campi, probabilmente si vorrà per scorrere su di loro quando li copia su l'altro modello. Lo lascerò a te.

+0

Cosa succede quando il modello che si sta salvando è Utente e AFAIK non è una buona idea farlo sottoclasse. Diciamo che ho una funzione post_save con il mittente come utente e all'interno di quella funzione voglio aggiornare l'istanza. Chiamando nuovamente salva a sua volta si attiva il post_save e il server locale si ferma. – Marconi

+0

Meglio ancora invece di 'model.save_dupe()' basta fare 'super (YourModel, model) .save()'. –

4

Un altro modo per gestire questo è rimuovere l'ascoltatore durante il salvataggio. Quindi:

class Foo(models.Model): 
    ... 

def foo_post_save(instance): 
    post_save.disconnect(foo_post_save, sender=Foo) 
    do_stuff_toSaved_instance(instance) 
    instance.save() 
    post_save.connect(foo_post_save, sender=Foo) 

post_save.connect(foo_post_save, sender=Foo) 
+0

Hai salvato la mia giornata, grazie! –

7

@ShawnFumo Scollegare un segnale è pericoloso se lo stesso modello viene salvato altrove, allo stesso tempo, non fare così!

@Aram Dulyan, la soluzione funziona ma non consente di utilizzare segnali così potenti!

Se si desidera evitare la ricorsione e continuare a utilizzare segnali(), un modo semplice per procedere è impostare un attributo nell'istanza corrente per impedire l'attivazione dei segnali imminenti.

Questo può essere fatto utilizzando un semplice decoratore che verifica se il dato caso ha il 'skip_signal' attributo , e se così impedisce il metodo venga chiamato:

from functools import wraps 

def skip_signal(): 
    def _skip_signal(signal_func): 
     @wraps(signal_func) 
     def _decorator(sender, instance, **kwargs): 
      if hasattr(instance, 'skip_signal'): 
       return None 
      return signal_func(sender, instance, **kwargs) 
     return _decorator 
    return _skip_signal 

Ora può utilizzare questo way:

from django.db.models.signals import post_save 
from django.dispatch import receiver 

@receiver(post_save, sender=MyModel) 
@skip_signal() 
def my_model_post_save(sender, instance, **kwargs): 
    # you processing 
    pass 

m = MyModel() 
# Here we flag the instance with 'skip_signal' 
# and my_model_post_save won't be called 
# thanks to our decorator, avoiding any signal recursion 
m.skip_signal = True 
m.save() 

Spero che questo aiuti.

+0

Non dimenticare di "skip_signal" dopo il salvataggio. –