2012-07-14 18 views
9

que tienen una devolución de llamada de la señal en Django:django señales desactivar temporalmente

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

pero en el cuerpo de la función que estoy usando .save() en modelos MediumCategory que couses que la señal se envía de nuevo. ¿Cómo puedo desactivarlo? la solución perfecta sería una declaración with con algo de "magia" dentro.

ACTUALIZACIÓN: Esa es la solución final, si alguien está interesado.

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) 

Respuesta

7

Tal vez me equivoque, pero creo que category.save() no es necesaria en el código, agregar() es suficiente porque el cambio se hace en descendiente pero en la categoría.

Además, para evitar señales se puede:

  • Disconnect signal y volver a conectar.
  • update Uso: Descendant.objects.filter(pk = descendant.pk).update(category = category)
+0

bien, eso es lo que estaba buscando: 'disconnect' es la solución, ponerlo en' con' declaración es una cuestión de pureza :) Pero después de eliminar 'save()', 'disconnect' no es necesario. Perfecto. – bartek

+0

tienes razón, 'save()' no es necesario. – bartek

9

@danihp desconectar una señal no es una solución SECO y consistente, como el uso de update() en lugar de Guardar().

Para deshabilitar una señal en su modelo, una manera simple de hacerlo es establecer un atributo en la instancia actual para evitar el disparo de las próximas señales.

Esto se puede hacer usando un decorador simple que comprueba si la instancia dada tiene la 'skip_signal' atributo, y si es así evita que el método que se llame:

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 

Ahora puede utilizar esta manera:

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

Espero que esto ayude.

+0

Muchas gracias, me ayudó mucho. –

Cuestiones relacionadas