Built-in Signals
Django comes with a variety of built-in signals that allow you to hook into different events during the lifecycle of models, forms, and request handling. These signals provide a powerful mechanism to respond to changes or actions across different components of your application, without modifying the core functionality of the components themselves.
Why Use Built-in Signals?
Using Django's built-in signals allows you to listen to important events like model saves, deletions, and request phases. By connecting receivers to these signals, you can automate tasks such as logging, notification systems, cleaning up data, or performing post-processing after a model action.
Common Built-in Signals in Django
Django provides several built-in signals for handling various stages of the model lifecycle and request/response cycle. Below are some of the most commonly used built-in signals:
- pre_save: Triggered just before a model instance is saved.
- post_save: Triggered immediately after a model instance is saved.
- pre_delete: Triggered before a model instance is deleted.
- post_delete: Triggered after a model instance is deleted.
- m2m_changed: Triggered when a many-to-many relationship changes (e.g., adding or removing items).
- request_started: Triggered when a request starts.
- request_finished: Triggered when a request is finished and a response is sent back to the client.
Detailed Breakdown of Common Signals
pre_save
The pre_save
signal is sent just before an instance of a model is saved. This is useful when you need to perform some actions or validations right before an object is saved to the database.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import MyModel
@receiver(pre_save, sender=MyModel)
def pre_save_handler(sender, instance, **kwargs):
# Action to perform before the model is saved
print(f'About to save: {instance}')
post_save
The post_save
signal is triggered immediately after a model instance is saved. It’s commonly used for tasks that need to occur after the model has been written to the database, such as sending notifications or creating related records.
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import MyModel
@receiver(post_save, sender=MyModel)
def post_save_handler(sender, instance, created, **kwargs):
if created:
print(f'New instance created: {instance}')
else:
print(f'Instance updated: {instance}')
pre_delete
The pre_delete
signal is sent just before a model instance is deleted. This is helpful when you need to perform cleanup or take action before an object is removed from the database.
from django.db.models.signals import pre_delete
from django.dispatch import receiver
from .models import MyModel
@receiver(pre_delete, sender=MyModel)
def pre_delete_handler(sender, instance, **kwargs):
# Action to perform before the model is deleted
print(f'Preparing to delete: {instance}')
post_delete
The post_delete
signal is triggered after a model instance has been deleted from the database. This is often used to perform tasks like cleaning up associated data or files.
from django.db.models.signals import post_delete
from django.dispatch import receiver
from .models import MyModel
@receiver(post_delete, sender=MyModel)
def post_delete_handler(sender, instance, **kwargs):
# Action to perform after the model is deleted
print(f'Instance deleted: {instance}')
m2m_changed
The m2m_changed
signal is sent whenever there are changes to a many-to-many relationship in Django. For instance, this signal is triggered when items are added, removed, or cleared from a many-to-many field.
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from .models import MyModel
@receiver(m2m_changed, sender=MyModel.some_m2m_field.through)
def m2m_changed_handler(sender, instance, action, **kwargs):
if action == 'post_add':
print(f'Items added to the relationship: {instance}')
request_started
The request_started
signal is triggered whenever a new HTTP request is received by Django. This signal can be useful for logging request activity or performing actions before request processing begins.
from django.core.signals import request_started
from django.dispatch import receiver
@receiver(request_started)
def request_started_handler(sender, **kwargs):
print('A new request has started!')
request_finished
The request_finished
signal is sent when Django finishes processing a request and sends the response back to the client. This can be useful for logging response times or clearing resources after a request has been handled.
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def request_finished_handler(sender, **kwargs):
print('The request has finished processing.')
Conclusion
Django’s built-in signals provide a powerful way to respond to specific events in your application without the need for tight coupling between components. Whether you’re working with model lifecycle signals like pre_save
and post_save
or request-related signals like request_started
and request_finished
, understanding how to use these signals can help you build more efficient and modular applications.
In the next sections, we will cover creating custom signals and more advanced use cases for signals in Django.