Django post save signal getting called twice despite uid
Asked Answered
A

4

11

I have registered my signal with the callback using the @receiver decorator

@receiver(post_save, sender=User, dispatch_uid='ARandomUniqueString') 
def do_callback(sender, **kwargs):

I have put the from app.signals import * code in __init__.py and I can see that it gets imported twice and I do not think there is a good way to fix it, possibly happening due to installed apps in settings.py. I cannot understand why despite using dispatch_uid and the modelInstance.save being invoked only once, it still runs do_callback twice. Any suggestions?

Absurdity answered 17/8, 2012 at 20:41 Comment(0)
A
15

Ok so I moved the import to views.py (or models.py and while it was getting imported only once, it was getting called twice.

The problem was that the post_save signal was getting called when the object was created as well as saved. I have no idea why so I added a workaround which now works

created = False

    #Workaround to signal being emitted twice on create and save
    if 'created' in kwargs:
        if kwargs['created']:
            created=True

    #If signal is from object creation, return
    if created:
        return

Edit:

post_save was getting called twice because I used .create(...) which is equivalent to __init__(...) and .save().

Conclusion

dispatch_uid does work and doing single imports is still a good practice.

Absurdity answered 17/8, 2012 at 21:12 Comment(2)
How to differentiate between init when created = False, and when we update/modify an object which also returns created=False?Sr
@Pratik Mandrekar which file did you put the workout if statement in? I think I may be facing a problem now similar to what you face back then.Wizardly
D
3

I had the same problem with post_save and also post_delete signals. It seems that the session object and LogEntry object were being saved as well creating multiple signals despite setting the dispatch_uid.

What worked for me was:

from django.contrib.admin.models import LogEntry
from django.contrib.sessions.models import Session

....

if sender in [LogEntry, Session]:
    return 
else:
    # do your thing here
Demisemiquaver answered 23/4, 2013 at 12:56 Comment(0)
C
3

I just encountered the same problem. I have a receiver that does something important which must be done only once for each new creation of a model instance in Django. So, I used the post_save signal, but that was being called twice for the creation of each new model instance which I was doing like Profile.objects.create(...). The solution to this problem is the created flag which comes with kwargs. Here's how you can use that flag to make sure your intended action is taken only once:

@receiver(post_save, sender=Profile)
def publish_auction(sender, **kwargs):
    if kwargs['created']:
        kwargs['instance'].send_email_confirmation()

I tried the dispatch_uid suggestion from Django docs. It didn't work, but the code I pasted above works.

Cribbage answered 1/6, 2017 at 21:27 Comment(0)
T
1

I have put the from app.signals import * code in __init__.py

You should not put anything in your __init__.py file.

If you remove this from __init__.py, and add it to the bottom of your models.py, it should solve your problem.

You should also avoid "blind" imports from foo import *

Tracheitis answered 17/8, 2012 at 20:57 Comment(1)
My signals.py uses models.py and while there is stuff I could do to avoid this circular dependency it goes against the flow. Solved it my only importing the do_callback in __init__.py instead of doing from app.sgnals import *Absurdity

© 2022 - 2024 — McMap. All rights reserved.