Django signals are a powerful feature that allows certain senders to notify a set of receivers when certain actions occur. They're essentially a way to allow decoupled applications to get notified when certain events happen elsewhere in the application.
Key Concepts
-
Signal: A signal is an object that represents a particular event (like a model being saved or deleted).
-
Sender: The component that dispatches (sends) the signal when an event occurs.
-
Receiver: A Python function (or method) that gets called when the signal it's connected to is sent.
Common Built-in Signals
Django comes with several built-in signals, primarily related to model lifecycle events:
-
pre_save&post_save: Sent before/after a model'ssave()method is called -
pre_delete&post_delete: Sent before/after a model'sdelete()method is called -
m2m_changed: Sent when a ManyToManyField is changed -
request_started&request_finished: Sent when HTTP requests start/complete
How to Use Signals
1. Connecting Receivers
First, you need to define a receiver function and connect it to a signal:
from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(post_save, sender=MyModel)
def my_handler(sender, instance, created, **kwargs):
"""
This function will be called after a MyModel instance is saved.
- 'created' is True if this is a new record
- 'instance' is the actual instance being saved
"""
if created:
print(f"A new {instance} was created!")
else:
print(f"{instance} was updated!")
2. Where to Put Signal Code
It's recommended to put signal handlers in a signals.py file in your app, and then import them in your app's apps.py ready() method:
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
import myapp.signals # this imports and registers the signals
3. Creating Custom Signals
You can also define your own signals:
# myapp/signals.py
from django.dispatch import Signal
# Define the signal
order_completed = Signal(providing_args=["order", "user"])
# Receiver function
@receiver(order_completed)
def handle_order_completion(sender, order, user, **kwargs):
print(f"Order {order} completed by {user}")
Then dispatch it somewhere in your code:
order_completed.send(sender=self.__class__, order=order, user=request.user)
Best Practices
-
Keep signal handlers lightweight: They execute synchronously and can slow down your application.
-
Be careful with infinite loops: Saving an object in a
post_savehandler will trigger anotherpost_savesignal. -
Document your signals: Especially custom ones, so other developers know when they fire.
-
Consider alternatives: Sometimes signals are overkill - simple method calls or overriding model methods might be cleaner.
When to Use Signals
Signals are particularly useful when:
-
You want to perform actions when models change, but can't modify the model code
-
You need multiple unrelated apps to react to the same event
-
You're developing a reusable app that needs to hook into Django's core events