Django post save signal getting called twice despi

2019-02-09 17:52发布

问题:

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?

回答1:

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.



回答2:

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


回答3:

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 *



回答4:

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.