2012-07-14 3 views
9

Ich habe ein Signal Rückruf in django:django vorübergehend deaktivieren Signale

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

aber in der Funktion Körper Ich verwende .save() auf Modelle MediumCategory die Couses, dass das Signal wieder ausgelöst wird. Wie kann ich es deaktivieren? die perfekte Lösung wäre eine with Aussage mit etwas "Magie" innen.

UPDATE: Das ist endgültige Lösung, wenn jemand interessiert.

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) 

Antwort

7

Vielleicht bin ich falsch, aber ich denke, dass category.save() nicht in Ihrem Code erforderlich ist, fügen Sie() ist genug da Änderung Nachkomme gemacht wird, sondern in der Kategorie.

Auch, um Signale vermeiden Sie können:

  • Disconnect signal und wieder an.
  • Verwendung update: Descendant.objects.filter(pk = descendant.pk).update(category = category)
+0

ok, das ist, was ich gesucht habe: 'disconnect' ist die Lösung, es in' with' Aussage zu setzen ist eine Frage der Reinheit :) Aber nach dem Entfernen 'save()', 'disconnect' wird nicht benötigt. Perfekt. – bartek

+0

Sie haben Recht, 'save()' wird nicht benötigt. – bartek

9

@danihp ein Signal zu trennen ist keine DRY und konsistente Lösung, wie Update mit() anstelle von save().

Um ein Signal in Ihrem Modell zu deaktivieren, können Sie einfach ein Attribut für die aktuelle Instanz festlegen, um zu verhindern, dass anstehende Signale ausgelöst werden.

Dies kann eine einfache Dekorateur erfolgen verwenden, wenn die gegebene Instanz prüft das ‚skip_signal‘ Attribut, und wenn ja, von der Methode verhindert aufgerufen wird:

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 

Jetzt können Sie es diese verwenden weg:

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

Hoffnung Das hilft.

+0

Vielen Dank, es hat mir sehr geholfen. –