Content Index
- What are Django Signals
- Types of Signals
- Model Signals
- Migrations Signals
- Request/Response Signals
- Test Signals
- Database Signals
- The signal dispatcher
- Receivers
- How to implement a Signal in Django step by step
- Register the Signals
- Practical example of Django Signals
- Automatically execute the signal
- Good practices when using Signals
- When to use signals and when to avoid them
- Frequently asked questions about Django Signals
- Conclusion
Django includes a package to handle application signals (just like using the shell, the Python Interactive Console in Django), which allows the application to receive notifications when certain actions occur, such as creating a post, registering a user, among others. When any of these actions happen, we can trigger this signal to execute a function that performs an action like logging, sending an email, etc.
Signals are nothing more than configurable events or messengers that inform about important occurrences in our application; they are a form of communication between different parts of the application. We will explain what Signals are, how they work, and how you can take advantage of them to keep your code organized and efficient.
Signals are a form of communication between different parts of your application; with signals, it is possible to notify a set of receivers that something has occurred.
As an example, assuming we have a blog and you want to send an email every time someone registers, we can use Signals in Django. The event is the registration, which triggers a signal that is connected to a function that sends the email; this action can be any other operation, such as sending a notification or another custom task.
Using signals is very easy; we just need to create a function that should be executed when the event occurs. This function is the receiver, and we specify it using the decorator from django.dispatch import receiver, for which we set two parameters: the type of signal and the model.
What are Django Signals
Django signals are events that allow notifying different parts of the application when a specific action occurs.
In other words, they work like configurable messengers that report on important events within the system.
For example:
- creating a user
- saving a model
- deleting a record
- initiating an HTTP request
When any of these events occur, Django can trigger a signal that will be captured by a receiver function, which will execute an action.
A common example is sending an email when a user registers on the platform. In that case:
- The user registers.
- Django triggers a signal.
- A receiver receives that signal.
- A function that sends the email is executed.
This mechanism allows keeping the code decoupled and organized, avoiding the addition of extra logic directly into models or views.
Types of Signals
We have different types of signals, all of which you can use to perform custom actions before/after the corresponding actions are taken, and we can classify them into five types:
- For models
- Migrations
- Requests/responses
- Testing
- Database
Model Signals
Signals related to models:
- pre_init: Activated just before a new instance of the associated model is created.
- post_init: Activated after a new instance of a model has been created.
- pre_save: Activated before saving a model instance to the database.
- post_save: Executed after a model instance has been saved to the database. It is useful for performing post-save actions, such as sending notifications or updating related models.
- pre_delete: Triggered before deleting a model instance. You can use it to perform actions prior to deletion, such as freeing resources or related records.
- post_delete: Executed after deleting a model instance. You can use it to perform tasks after deletion, such as freeing resources or related records.
- m2m_changed: This signal is activated when many-to-many (ManyToMany) relationships are modified.
- class_prepared: Triggered when a model class is fully prepared and ready for use. You can use it to perform additional configurations on model classes.
Migrations Signals
Signals related to migrations:
- pre_migrate: Executed before applying migrations to the database.
- post_migrate: Activated after applying migrations to the database. You can use it to perform actions after migration, such as loading initial data or updating schemas.
Request/Response Signals
Signals related to HTTP requests and responses:
- request_started: Triggered at the beginning of an HTTP request.
- request_finished: Executed at the end of an HTTP request.
- got_request_exception: Activated when an exception occurs during request processing. Useful for handling errors and logging the exception.
Test Signals
Signals related to unit testing:
- setting_changed: This signal is sent when the value of a setting is changed. Useful for handling changes in application settings during tests.
- template_rendered: Sent when the test system renders a template.
Database Signals
Signals related to database connections:
- connection_created: Activated when a database connection is established.
When receiving these signals, there are specific parameters that you can explore in the official documentation:
https://docs.djangoproject.com/en/dev/ref/signals/
How Django Signals work internally
To better understand how signals work, it is important to know their two main components:
Dispatcher
Receivers
The signal dispatcher
The dispatcher is the system responsible for sending signals when an event occurs within the framework.
For example, when a model is saved to the database, Django can automatically trigger the signal:
post_saveThe dispatcher will notify all registered receivers that are listening for that event.
Receivers
Receivers are functions that react when a signal is triggered.
These functions connect to signals using the decorator:
from django.dispatch import receiverA receiver listens for a specific type of signal and is usually also limited to a concrete model.
When the event occurs, Django automatically executes the registered function.
How to implement a Signal in Django step by step
Creating a signal in Django is quite simple. Basically, we need to:
- create a receiver
- connect it to a signal
- register it in the application
Create the signals.py file
First, we create a file named:
signals.pyinside our application.
For example:
channels/alert/signals.pyThe first thing we must do is define a Signal; for this, we use a function that is activated when a specific event occurs, for example, the creation of an alert:
channels/alert/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Alert
@receiver(post_save, sender=Alert)
def save_user_profile(sender, instance, **kwargs):
print('Alert Created')This function will act as a receiver of the signal, meaning it will execute when the corresponding signal runs. In this example, the corresponding signal is the creation (post_save) of an alert; it will display a message in the console, but the task to be performed can be any other operation, such as sending an email or other database operations.
Register the Signals
In the apps.py file of your application, we must register the signals.py file in the ready() method:
channels/alert/apps.py
from django.apps import AppConfig
class AlertConfig(AppConfig):
name = 'alert'
def ready(self):
import alert.signalsIn this example method, we create an alert:
channels/alert/views.py
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from .models import Alert
@login_required
def create(request):
alert = Alert()
alert.content = 'Test'
alert.user = request.user
alert.save()
return HttpResponse('Response')And we create the route for the previous view:
channels/alert/urls.py
urlpatterns = path('dashboard/create',views.create),Now, every time we create an alert, the previous signal will be executed automatically, and this is an important factor: every time an alert is created, for example, from Django Channels, or through the consumer we created in the previous section, the previous signal will execute. As we mentioned before, a signal is nothing more than a function associated with an event, except that the signal executes automatically when the event occurs.
Practical example of Django Signals
Let's look at a simple example to understand the whole flow.
Create an alert when a model is saved
Suppose we have a model named Alert and we want to execute an action every time a new alert is created.
In the view, we could have something like this:
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from .models import Alert
@login_required
def create(request):
alert = Alert()
alert.content = 'Test'
alert.user = request.user
alert.save()
return HttpResponse('Response')Every time this view is executed, an alert is created and saved in the database.
Automatically execute the signal
When the following is executed:
alert.save()Django automatically triggers the signal:
post_saveThis event is captured by our receiver defined in signals.py, which will execute the corresponding function.
This means that the signal will execute regardless of where the object is created.
For example:
- from a view
- from Django Admin
- from a script
- from a Django Channels consumer
This automatic behavior is one of the reasons why signals are so useful for automating processes within an application.
Good practices when using Signals
Although signals are very useful, it is important to use them correctly.
Some good practices are:
- Keep signals simple
- Receivers should execute small, specific logic.
- If the logic is complex, it is better to delegate it to an external service or function.
- Avoid hidden dependencies
- One of the problems with signals is that they can create behaviors that are not obvious to other developers.
- That is why it is recommended to document them well.
- Avoid critical logic in signals
- It is not recommended to use signals for critical system operations, as they can run at unexpected times.
When to use signals and when to avoid them
Signals are useful when we want to react to events without directly modifying the code that generates them.
They are ideal for:
- sending notifications
- logging events
- updating related models
- triggering asynchronous tasks
However, they are not always the best option.
It is advisable to avoid them when:
- the logic is part of the main flow
- the behavior must be explicit
- the system may become difficult to maintain
Frequently asked questions about Django Signals
- What are signals in Django?
- They are an event system that allows functions to be executed automatically when an action occurs within the application.
- Where are signals registered in Django?
- They are usually defined in a signals.py file and registered in apps.py inside the ready() method.
- What is the most used signal?
- One of the most common is post_save, which executes after saving a model.
- Can I create custom signals?
- Yes. Django allows creating custom signals using the django.dispatch module.
Official documentation:
Conclusion
Signals in Django are a powerful tool for building decoupled applications and automating internal system tasks.
They allow reacting to important events within the application without directly modifying the code that generates them, which helps maintain a cleaner and more modular architecture.
Used well, signals can simplify many common tasks, such as sending notifications, logging events, or executing actions after saving models.
However, it is also important to use them with moderation and apply good practices to avoid hidden behaviors that could hinder the project's maintenance.
https://docs.djangoproject.com/en/dev/topics/signals/
https://docs.djangoproject.com/en/dev/ref/signals/
Next step, learn how to create a Rest API in Django (using Django Rest Framework)