我在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)
但是在函数体中我在模型.save()
上使用MediumCategory
来表示信号再次被调度。我该如何禁用它;完美的解决方案是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)
答案 0 :(得分:17)
@danihp不是 DRY 和一致的解决方案,例如使用update()而不是save()。
要禁用模型上的信号,一个简单的方法是在当前实例上设置属性以防止即将发出的信号。
这可以使用一个简单的装饰器来完成,该装饰器检查给定的实例是否具有' 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()
希望这有帮助。
答案 1 :(得分:11)
也许我错了,但我认为你的代码中不需要category.save()
,add()就足够了,因为在后代但在类别中进行了更改。
另外,为避免信号,您可以:
Descendant.objects.filter( pk = descendant.pk ).update( category = category )