Manuel Faux
Manuel Faux

Reputation: 2457

Prevent signal sending for specific save() calls

I'm performing some persistence actions with the model's object when receiving a post_save signal, which includes a call to save(). Obviously the save() call sends a post_save signal again and I'm landing in a signal recursion.

Is there a way to prevent Django to send a post_save signal on a specific save() call? Or can I detect in my signal callback if a call was "looped"?

That didn't work

I tried to modify the model's object by adding a attribute, but it seems like django.db.models.base.Model.save_base passes a "sanitized" object to the signal callback, which does not contain that attribute anymore:

def callback(sender, **kwargs):
    instance = kwargs['instance']
    if not hasattr(instance, 'no_signal'):
        # (...) Perform actions
        instance.no_signal = True
        instance.save()
post_save.connect(callback, dispatch_uid='post_save_callback')

Background

The complete situation is a bite more complex, than just receiving the signal, modifying the object and persisting it. In fact I have two identical Django instances (on different servers) which exchange created or modified objects (via ØMQ) and persist it in their own database. I don't want to use any kind of DB synchronization, since the application needs to be deployable easily and should even work with sqlite.

Since this should work for all models, regardless if they were modified/created via a view or the admin app, I'd prefer finding a way to use the post_save signal rather than introducing an own one, which needs to be triggered in various points in the project (including the User model).

Upvotes: 6

Views: 2612

Answers (2)

Manuel Faux
Manuel Faux

Reputation: 2457

I solved the problem by calling save_base(raw=True) instead of save() and then checked the kwarg['raw'] in the signal handler:

def receive_signal(sender, **kwargs):
    instance, raw = kwargs['instance'], kwargs['raw']
    if not raw:
        # Send the instance to the other Django node

Actually, I'm using django.core.serializers.deserialize to deserialize my received objects and then perform a save() on the deserialized objects, which calls the save_base(raw=True) of the model.

See also: https://stackoverflow.com/a/22560210/145013

Upvotes: 6

jb.
jb.

Reputation: 23955

First of all: if you deploy your django programs in a way that does no multithreading (you can do multiprocessing!) you can disconnect signals at will, and you'll not lose any singals from other threads.

If this is not an option you may also use some global thread-local state, that will contain primaty keys (and possibly types) of models that have signals suppressed.

Upvotes: 1

Related Questions