Introduction to Django Signals
In this page:
What Is a Django Signal?
A signal is Django's built-in publish/subscribe system. One piece of code sends a signal when something happens, and any number of receiver functions elsewhere can listen for it and react, without the sender needing to know who's listening.
Note: Signals are great for cross-app hooks, like sending a welcome email when any app creates a User.
Example: What Is a Django Signal?
A signal is Django's built-in publish/subscribe system. One piece of code sends a signal when something happens, and any number of receiver functions elsewhere can listen for it and react, without the sender needing to know who's listening.
import django.dispatch
order_placed = django.dispatch.Signal()
def notify_shipping(sender, order_id, **kwargs):
print(f"Notify shipping: order {order_id} placed")
order_placed.connect(notify_shipping)
order_placed.send(sender=None, order_id=42)
{# 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. #}
Signals vs Direct Function Calls
Before reaching for a signal, ask whether a plain function call would be simpler. A direct call is easier to trace and debug; a signal is better when the sender genuinely shouldn't know who reacts to it.
Example: Signals vs Direct Function Calls
Before reaching for a signal, ask whether a plain function call would be simpler. A direct call is easier to trace and debug; a signal is better when the sender genuinely shouldn't know who reacts to it.
# Without signals: caller directly imports and calls
from shipping.utils import notify_shipping
def place_order(order_id):
save_order(order_id)
notify_shipping(order_id)
{# 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. #}
Built-in Signals Overview
Django ships several ready-made signals for model lifecycle events, such as post_save, pre_save, and post_delete from django.db.models.signals.
Example: Built-in Signals Overview
Django ships several ready-made signals for model lifecycle events, such as post_save, pre_save, and post_delete from django.db.models.signals.
from django.db.models.signals import post_save, post_delete
def log_save(sender, instance, **kwargs):
print(f"{sender.__name__} saved: {instance.pk}")
post_save.connect(log_save)
{# 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 a Receiver with @receiver
The @receiver decorator is the most common shorthand for connecting a function to a signal, optionally scoped to one model with sender=.
Example: Connecting a Receiver with @receiver
The @receiver decorator is the most common shorthand for connecting a function to a signal, optionally scoped to one model with sender=.
from django.dispatch import receiver
from django.db.models.signals import post_save
from django.contrib.auth.models import User
@receiver(post_save, sender=User)
def welcome_new_user(sender, instance, created, **kwargs):
if created:
print(f"Welcome, {instance.username}!")
{# 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. #}
- Believing signals run in a guaranteed order across multiple receivers.
- Overusing signals for simple logic that a plain function call would handle more clearly.
- Forgetting to import the signals module so its receivers actually get connected.
- Signals let one part of Django notify other decoupled parts when something happens.
- django.dispatch.Signal lets you define and send fully custom signals.
- Receivers are just functions connected to a signal with @receiver or .connect().
- Signals are convenient for cross-app decoupling but add an extra layer of indirection to trace.
Chapter Quiz — Complete all 8 topics to unlock
0/8 topics done
Complete these topics first: