← Back to Django Course | Chapter 12: Signals & Middleware | Lesson 4 of 8

Connecting Signals to Receivers

Connecting a receiver is like giving the bell someone's phone number so it knows who to call when it rings.

Two Ways to Connect a Receiver

You can connect a receiver with the @receiver decorator, or by calling signal.connect(func, sender=Model) directly — both do the same thing.

Example: Two Ways to Connect a Receiver

You can connect a receiver with the @receiver decorator, or by calling signal.connect(func, sender=Model) directly — both do the same thing.

markup
from django.dispatch import receiver
from django.db.models.signals import post_delete
from .models import Comment

@receiver(post_delete, sender=Comment)
def log_delete(sender, instance, **kwargs):
    print(f"Comment {instance.pk} deleted")

# Equivalent without the decorator:
# post_delete.connect(log_delete, sender=Comment)
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Making Sure Receivers Get Registered

A receiver only works if Python actually imports the module it's defined in. The standard place to do this is AppConfig.ready(), which Django calls once at startup.

Example: Making Sure Receivers Get Registered

A receiver only works if Python actually imports the module it's defined in. The standard place to do this is AppConfig.ready(), which Django calls once at startup.

markup
from django.apps import AppConfig

class BlogConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "blog"

    def ready(self):
        import blog.signals
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Connecting the Same Receiver to Multiple Senders

One receiver function can be connected to more than one model, either by calling connect() several times or looping over a list of senders.

Example: Connecting the Same Receiver to Multiple Senders

One receiver function can be connected to more than one model, either by calling connect() several times or looping over a list of senders.

markup
from django.db.models.signals import post_save
from .models import Article, Comment

def log_save(sender, instance, **kwargs):
    print(f"{sender.__name__} saved")

for model in (Article, Comment):
    post_save.connect(log_save, sender=model)
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Disconnecting a Receiver

Receivers can be removed with signal.disconnect(), which is mainly useful in tests where you want to temporarily silence a side effect.

Example: Disconnecting a Receiver

Receivers can be removed with signal.disconnect(), which is mainly useful in tests where you want to temporarily silence a side effect.

markup
from django.db.models.signals import post_save
from .signals import log_save
from .models import Article

post_save.disconnect(log_save, sender=Article)
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}
Common Mistakes
  1. Defining a receiver function but never connecting it, or connecting it in a module that's never imported.
  2. Connecting signals inside a model's save() method, which reconnects the same receiver on every save.
  3. Not using sender= to scope the receiver, causing it to run for every model in the project.
Chapter Summary
  • A receiver is just a normal Python function with a matching signature.
  • Use @receiver(signal, sender=Model) or signal.connect(func, sender=Model) to connect it.
  • Signal receivers must be imported somewhere for Django to actually register them, usually via AppConfig.ready().
  • Scope receivers to a specific sender to avoid unexpected side effects on unrelated models.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.