Search code examples
djangomodelsignals

django temporarily disable signals


I have a signal callback in 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)

...but in the signal handler's body I'm using .save() on the same model MediumCategory. This causes the signal to be dispatched again. How can I disable it?

The perfect solution would be a with statement with some 'magic' inside.

UPDATE: Here is my final solution, if anyone interested:

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)

Solution

  • Perhaps I'm wrong, but I think that category.save() is not needed in your code, add() is enough because change is made in descendant but in category.

    Also, to avoid signals you can:

    • Disconnect signal and reconnect.
    • Use update: Descendant.objects.filter( pk = descendant.pk ).update( category = category )