Opened 3 weeks ago

Closed 13 days ago

#28970 closed New feature (wontfix)

Option to suppress signals on save to avoid loop

Reported by: Gustavo Henrique de Almeida Gonçalves Owned by: nobody
Component: Database layer (models, ORM) Version: 2.0
Severity: Normal Keywords: signal suppress save recursion loop problem
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Tim Graham)

Calling instance.save() inside a post_save signal receiver causes a loop and max recursion error. This problem is very easy to fall into and the best solution at the moment is call the QuerySet update() method, so that the post_save signal is not called in this case.

This is a very ugly workaround. I think Django should give the option to call save() and explicitly suppress signal emitting. For example:

instance.save(post_save=False)

In the above method call, post_save signal would not be sent, and the loop problem would not occurs.

Change History (4)

comment:1 Changed 3 weeks ago by Tim Graham

Description: modified (diff)

Resaving the object in post_save() doesn't sound like a common use case (or a wise thing to do, from a performance standpoint). Can you explain your use case?

comment:2 Changed 2 weeks ago by Victor Gutemberg

I can think about a case when working with asynchronous tasks, for example:

signals.py

from django.dispatch import receiver
from django.db.models.signals import post_save

from .models import ReferenceModel
from .tasks import async_task


@receiver(post_save, sender=ReferenceModel)
def process_image(sender, instance, created, **kwargs):
    async_task.delay(instance.id)

tasks.py

from celery.decorators import task
from .models import ReferenceModel

@task
def async_task(object_id):
    instance = ReferenceModel.objects.get(object_id)
    # do some update
    instance.save()

In this case you might not want the instance.save() inside the task to generate a post_save signal, otherwise the same task would be performed again.

comment:3 Changed 2 weeks ago by Tim Graham

In that case, I think async_task should set an attribute like instance._processed = True which the process_image() handler could check for.

comment:4 Changed 13 days ago by Tim Graham

Resolution: wontfix
Status: newclosed

Feel free to raise the idea on the DevelopersMailingList if you disagree with the wontfix decision.

Note: See TracTickets for help on using tickets.
Back to Top