Search code examples
djangosignals

Django 4.2 : `receiver` Decorator Does Not Receive Signal


I am experimenting with Django Signals.

In the documentation, it is written that there are two ways to connect a receiver to a signal.

  1. Using Signal.connect method
  2. Using receiver decorator

Here's what I have implemented:

# models.py
from django.db import models
from django.dispatch import receiver

from .signals import demo_signal


class Demo(models.Model):
    demo = models.CharField("demo", max_length=50)

    def send_signal(self):
        demo_signal.send(self)
        print('signal sent')

    def connect_receiver(self):
        demo_signal.connect(signal_handler, sender=self)


@receiver(demo_signal, sender=Demo)
def signal_handler(**kwargs):
    print('signal handled')

# signals.py
from django.dispatch import Signal

demo_signal = Signal()

However, when I call send_signal method, I don't get signal handled printed out unless I call connect_receiver method first.

In [1]: demo = Demo.objects.get(pk=2)

In [2]: demo
Out[2]: <Demo: Demo object (2)>

In [3]: demo.send_signal()
signal sent

In [4]: 

And Interestingly enough, after implementing pre_delete_handler as follows, without connecting, calling delete method does call pre_delete_handler

@receiver(pre_delete, sender=Demo)
def pre_delete_handler(instance, **kwargs):
    print(instance, kwargs)
    print('pre delete')

Receiver does receive the signal without sender argument:

@receiver(pre_delete)
def pre_delete_handler(instance, **kwargs):
    print(instance, kwargs)
    print('pre delete')

But how can I make it listen to a specific Model? Why does sending signal does not call its receiver(decorated signal_handler) in my case?


Solution

  • Wow... Just a moment after posting this question, I have figured out why.

    So, if I want to call model specific receiver, I have to pass a class instead of an instance. That is, self.__class__.

    class Demo(models.Model):
        demo = models.CharField("demo", max_length=50)
    
        def send_signal(self):
            demo_signal.send(sender=self.__class__)
            print('signal sent')
    
    
    @receiver(demo_signal, sender=Demo)
    def signal_handler(**kwargs):
        print('demo signal handled')