Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django how to use the ``receiver`` decorator on a class instead on a function

Tags:

python

django

Using Django signals receiver decorator I have the following function.

@receiver(post_save)
def action_signal(sender, instance, created, **kwargs):
    pass

Is it possible to use the receiver decorator on a class instead on a function? The reason for this is I would like to have a __init__ method etc.

i.e. How can I get something like this to work...

class ActionSignals

    def __init__(self):
        self.stuff

    @receiver(post_save)
    def action_signal(sender, instance, created, **kwargs):
        print(self.stuff)
like image 771
MarkK Avatar asked Oct 14 '15 12:10

MarkK


2 Answers

Using the receiver decorator on a class method doesn't really make sense. When do you expect the object to be instantiated and the __init__ method to run?

You can use the manual method for connecting signals, instead of the receiver decorator.

First instantiate the object:

action_signal = ActionSignals()

Then you can use the connect method to connect the signal:

post_save.connect(action_signal.action_signal)
like image 60
Alasdair Avatar answered Nov 15 '22 00:11

Alasdair


Adding a class-based signal handler

You can do this:

class ActionSignals(object):
    def __init__(self, *args, **kwargs):
        # ...            

    def __call__(self, *args, **kwargs):
        print(self.stuff)

Then to connect the signal handler:

handler = ActionSignals()

post_save.connect(handler)

This makes use of python's "magic" __call__ method that allows you to use an instance of a class as a function.

Avoiding duplicates

Be careful about where you are adding the handlers in your code, as you might create duplicates. For example if you were to put the second bit of code in the module root, it would add a handler every time the module is imported.

To avoid this you can do the following:

post_save.connect(handler, dispatch_uid="my_unique_identifier")

As @Alasdair pointed out, you can add handlers in AppConfig.ready() (and this is the recommended place to do it), although generally it can be done anywhere if you take care not to create undesired duplicates.

See "Where should this code live?" in this doc.

like image 41
Ivan Avatar answered Nov 14 '22 22:11

Ivan