Predefined Signals in Django
In Django, signals allow certain senders to notify a set of receivers when certain actions have taken place. They are used to allow decoupled applications to get notified when certain events occur. Django provides several predefined signals that can be used to trigger actions during various phases of request/response processing.
1. What Are Signals in Django?
Signals are a form of event-driven programming where certain actions (like saving a model, user login, etc.) trigger corresponding functions. Django's signals framework allows different parts of a Django application to communicate with each other by sending and receiving notifications.
Signals are dispatched using the signal.send()
method, and receivers can listen to and handle these signals using decorators like @receiver
.
2. Predefined Django Signals
Django provides several built-in signals that are triggered at different points in the application's lifecycle. Below are some of the most commonly used predefined signals in Django:
2.1. pre_save
and post_save
Signals
The pre_save
signal is sent just before a model’s save()
method is called. The post_save
signal is sent right after the model’s save()
method is executed.
Example: Using pre_save
and post_save
# models.py
from django.db import models
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
# Receiver for pre_save signal
@receiver(pre_save, sender=Author)
def pre_save_handler(sender, instance, **kwargs):
print("Preparing to save author:", instance.name)
# Receiver for post_save signal
@receiver(post_save, sender=Author)
def post_save_handler(sender, instance, created, **kwargs):
if created:
print("New author created:", instance.name)
else:
print("Author updated:", instance.name)
In this example, the pre_save_handler
will execute before saving an Author
instance, and the post_save_handler
will execute after the Author
instance is saved.
2.2. pre_delete
and post_delete
Signals
Similar to the save signals, the pre_delete
and post_delete
signals are triggered just before and after an object is deleted from the database, respectively.
Example: Using pre_delete
and post_delete
# models.py
from django.db import models
from django.db.models.signals import pre_delete, post_delete
from django.dispatch import receiver
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.CASCADE)
# Receiver for pre_delete signal
@receiver(pre_delete, sender=Book)
def pre_delete_handler(sender, instance, **kwargs):
print("Preparing to delete book:", instance.title)
# Receiver for post_delete signal
@receiver(post_delete, sender=Book)
def post_delete_handler(sender, instance, **kwargs):
print("Book deleted:", instance.title)
The pre_delete_handler
is triggered just before a Book
instance is deleted, while the post_delete_handler
is triggered after it is deleted.
2.3. m2m_changed
Signal
The m2m_changed
signal is sent when a many-to-many (M2M) relationship is modified. It is triggered when items are added or removed from a many-to-many field.
Example: Using m2m_changed
# models.py
from django.db import models
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
class Tag(models.Model):
name = models.CharField(max_length=50)
class Post(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag)
# Receiver for m2m_changed signal
@receiver(m2m_changed, sender=Post.tags.through)
def m2m_changed_handler(sender, instance, action, **kwargs):
if action == 'post_add':
print(f"Tags added to post: {instance.title}")
elif action == 'post_remove':
print(f"Tags removed from post: {instance.title}")
Here, the signal handler listens to changes in the tags
many-to-many field of the Post
model. It prints a message whenever tags are added or removed from a post.
2.4. request_started
and request_finished
Signals
The request_started
signal is sent just before Django starts processing a request, while the request_finished
signal is sent after Django finishes processing a request.
Example: Using request_started
and request_finished
# signals.py
from django.core.signals import request_started, request_finished
from django.dispatch import receiver
import time
@receiver(request_started)
def request_started_handler(sender, **kwargs):
print("Request started at:", time.time())
@receiver(request_finished)
def request_finished_handler(sender, **kwargs):
print("Request finished at:", time.time())
This example logs the time at which each request starts and finishes, which can be helpful for debugging and performance monitoring.
3. Connecting and Disconnecting Signals
To connect a signal to a receiver function, you can use the @receiver
decorator or manually connect it using the signal.connect()
method. You can also disconnect a signal using signal.disconnect()
.
# Manual signal connection
from django.db.models.signals import pre_save
from myapp.models import Author
from myapp.signals import pre_save_handler
pre_save.connect(pre_save_handler, sender=Author)
# Disconnect signal
pre_save.disconnect(pre_save_handler, sender=Author)
4. Conclusion
Predefined signals in Django are an important tool for allowing different parts of your application to communicate with each other. By using signals, you can easily hook into various lifecycle events such as saving, deleting models, handling many-to-many relationships, and processing requests. Understanding how to work with Django’s signals system can greatly improve the flexibility and maintainability of your application.