Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to send a django signal from other signal

TL;DR:
I need a way to trigger a custom signal after the post_save signal, automatically, is there any way of doing it?


I'm currently developing a library for django that requires a lot of comes and goes with the post_save signal in django and I was wondering if it's possible to trigger another signal after the post_save so I could implement my own and not intervene the post_save in case a project that uses the library needs to do it.

So far I know that signals are expected to receive a class as a sender argument, and if I trigger manually the signal from the post_save I would be doing nothing (I'd still be intervening it). Is there any workaround for this? Am I missing something in the docs?

like image 869
CastleDweller Avatar asked Nov 30 '17 15:11

CastleDweller


People also ask

How do I call a signal in Django?

There are two ways to send signals in Django. To send a signal, call either Signal. send() (all built-in signals use this) or Signal. send_robust() .

Are Django signals asynchronous?

For the uninitiated, signals are synchronously (this will be important later) firing events that trigger handlers. There are a few common built in ones, we'll be discussing post_save and pre_save signals that are triggered whenever a Django model is saved.

Should you use Django signals?

The only reason to use signalsOnly use signals to avoid introducing circular dependencies. If you have two apps, and one app wants to trigger behaviour in an app it already knows about, don't use signals. The app should just import the function it needs and call it directly.


1 Answers

Although this may be possible by calling a signal manually from inside another signal like this:

post_save.send(MyModel, instance=a_mymodel_instance)

There are easier ways to do something like that:

Let us assume that you follow the file organization that I use in this answer: Django Create and Save Many instances of model when another object are created


Suggestion 1:

  • Let us assume that your first post_save does something to MyModel1 and another post_save signal does something to MyModel2 after some processing on the instance that triggered the first signal.
  • post_save is always send at the end of a save() method.
  • Organize your signals as follows:

    @receiver(post_save, sender=MyModel1)
    def mymodel1_signal (sender, instance, **kwargs):
        Do stuff on MyModel1 instance...
        Do also stuff on MyModel2 and then call MyModel2.save()
    
    
    @receiver(post_save, sender=MyModel2)
    def mymodel2_signal (sender, instance, **kwargs):
        Do stuff on MyModel2 instance...
    

    This way the mymodel2_signal signal will get triggered after the call to MyModel2.save() from mymodel1_signal.


Suggestion 2:

  • Use a mix of signals to achieve the correct "timing".
  • Let us assume that you want to start a process on MyModel2 before a MyModel1 get's saved
  • Use a pre_save and a post_save signal:

    @receiver(pre_save, sender=MyModel1)
    def mymodel1_signal (sender, instance, **kwargs):
        Do stuff on MyModel1 instance...
        Do also stuff on MyModel2 and then call MyModel2.save()
    
    
    @receiver(post_save, sender=MyModel2)
    def mymodel2_signal (sender, instance, **kwargs):
        Do stuff on MyModel2 instance...
    

Suggestion 3:

Use a MyModel2 method directly inside a MyModel1 post_save signal.

like image 62
John Moutafis Avatar answered Nov 14 '22 23:11

John Moutafis