2012-07-14 91 views
9

我有一个信号回调在Django:Django的暂时禁止信号

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

但在函数体中,我对couses该信号再次出动机型MediumCategory使用.save()。我如何禁用它;完美的解决方案将是一个with声明,里面有一些“魔力”。

更新: 这是最终的解决方案,如果有人感兴趣。

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) 

回答

7

也许我错了,但我认为category.save()不需要在你的代码中,添加()就足够了,因为变化在后代但在产品制造。

此外,为避免您能信号:

  • Disconnect signal并重新连接。
  • 使用updateDescendant.objects.filter(pk = descendant.pk).update(category = category)
+0

好的,这就是我要找的:'disconnect'是解决方案,把它放在'with'语句中是纯粹的问题:)但是在移除save()后,不需要disconnect。完善。 – bartek 2012-07-14 20:44:22

+0

你是对的,'save()'是不需要的。 – bartek 2012-07-14 20:44:54

9

@danihp断开信号不是DRY和一致的解决方案,例如,使用更新()而不是保存()。

要禁用模型上的信号,一个简单的方法是在当前实例上设置一个属性,以防止即将发生的信号触发。

这可以用一个简单的装饰来检查,如果给定的实例具有“skip_signal”属性来完成,如果是防止被调用的方法:

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 

现在,您可以使用它这个方式:

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

希望这会有所帮助。

+0

非常感谢,它帮助了我很多。 – 2016-02-08 21:55:02