2012-07-14 15 views
9

Ho un segnale di richiamata in django:Django segnali disabilitare temporaneamente

@receiver(post_save, sender=MediumCategory) 
def update_category_descendants(sender, **kwargs): 

    def children_for(category): 
     return MediumCategory.objects.filter(parent=category) 

    def do_update_descendants(category): 
     children = children_for(category) 
     descendants = list() + list(children) 

     for descendants_part in [do_update_descendants(child) for child in children]: 
      descendants += descendants_part 

     category.descendants.clear() 
     for descendant in descendants: 
      if category and not (descendant in category.descendants.all()): 
       category.descendants.add(descendant) 
       category.save() 
     return list(descendants) 

    # call it for update 
    do_update_descendants(None) 

ma nel corpo della funzione che sto utilizzando .save() su modelli MediumCategory che couses che il segnale viene inviato di nuovo. Come posso disabilitarlo; la soluzione perfetta sarebbe una dichiarazione with con un po 'di "magia" all'interno.

UPDATE: Ecco soluzione finale, se chiunque sia interessato.

class MediumCategory(models.Model): 
    name = models.CharField(max_length=100) 
    slug = models.SlugField(blank=True) 
    parent = models.ForeignKey('self', blank=True, null=True) 
    parameters = models.ManyToManyField(AdvertisementDescriptonParameter, blank=True) 
    count_mediums = models.PositiveIntegerField(default=0) 
    count_ads = models.PositiveIntegerField(default=0) 

    descendants = models.ManyToManyField('self', blank=True, null=True) 

    def save(self, *args, **kwargs): 
     self.slug = slugify(self.name) 
     super(MediumCategory, self).save(*args, **kwargs) 

    def __unicode__(self): 
     return unicode(self.name) 
(...) 
@receiver(post_save, sender=MediumCategory) 
def update_category_descendants(sender=None, **kwargs): 
    def children_for(category): 
     return MediumCategory.objects.filter(parent=category) 

    def do_update_descendants(category): 
     children = children_for(category) 
     descendants = list() + list(children) 

     for descendants_part in [do_update_descendants(child) for child in children]: 
      descendants += descendants_part 

     if category: 
      category.descendants.clear() 
      for descendant in descendants: 
       category.descendants.add(descendant) 
     return list(descendants) 

    # call it for update 
    do_update_descendants(None) 

risposta

7

Forse mi sbaglio, ma credo che category.save() non è necessaria nel codice, aggiungere() è sufficiente perché il cambiamento è fatto in discendente, ma nella categoria.

Inoltre, per evitare segnali è possibile:

  • Disconnect signal e ricollegare.
  • Uso update: Descendant.objects.filter(pk = descendant.pk).update(category = category)
+0

ok, questo è quello che stavo cercando: 'disconnect' è la soluzione, metterlo in istruzione' with' è una questione di purezza :) Ma dopo aver rimosso 'save()', 'disconnect' non è necessario. Perfezionare. – bartek

+0

hai ragione, 'save()' non è necessario. – bartek

9

@danihp scollegare il segnale non è una soluzione DRY e coerente, come l'utilizzo di update() al posto di save().

Per disabilitare un segnale sul modello, un modo semplice per procedere è impostare un attributo sull'istanza corrente per impedire l'attivazione dei segnali imminenti.

questo può essere fatto utilizzando un semplice decoratore che controlla se la data istanza ha il 'skip_signal' attributo , e se è così impedisce il metodo da essere 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 possibile utilizzare in 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): 
    instance.some_field = my_value 
    # Here we flag the instance with 'skip_signal' 
    # and my_model_post_save won't be called again 
    # thanks to our decorator, avoiding any signal recursion 
    instance.skip_signal = True 
    instance.save() 

Spero che questo aiuti.

+0

Grazie mille, mi ha aiutato molto. –

Problemi correlati