Signals in Django - potatoscript/django GitHub Wiki
📢 Django Signals – Listen & React
Django signals allow different parts of a Django application to communicate and react to events in the application. It's a way to make your app more modular, letting certain parts of your application listen for changes and react accordingly, without needing tight coupling between components. For example, you can automatically update something when a new user is registered, or send an email when a blog post is saved.
In this tutorial, we’ll cover how to set up and use Django signals in your projects. We'll break down everything step by step, with easy-to-understand explanations and examples.
🧩 Step 1: What Are Django Signals?
Django signals are used to allow certain actions (events) to trigger other actions (handlers). It’s like having a system where parts of the app can listen for changes and react to them when something happens.
Example:
Imagine you run a store and want to send a thank-you email whenever someone places an order. Instead of manually writing code every time an order is placed, you can use a signal to automatically send the email whenever the order is saved.
🧩 Step 2: How Django Signals Work
Django provides a signal framework that allows you to attach "listeners" (functions) to specific events (signals). When the event occurs, the listeners are called automatically.
There are two main parts in the signal framework:
- Sender – This is the part of the application that sends out the signal. It’s the event that is happening.
- Receiver (Listener) – This is the function that gets called when the signal is sent. It "reacts" to the event.
🧩 Step 3: Common Django Signals
Django includes several built-in signals that are sent by various parts of Django, like the database, user authentication, and more.
Here are some common signals:
django.db.models.signals.pre_save
– Sent before a model’s save() method is called.django.db.models.signals.post_save
– Sent after a model’s save() method is called.django.db.models.signals.pre_delete
– Sent before a model’s delete() method is called.django.db.models.signals.post_delete
– Sent after a model’s delete() method is called.django.core.signals.request_started
– Sent when a request is started.django.core.signals.request_finished
– Sent when a request finishes.
🧩 Step 4: Setting Up Signals in Django
Now let’s go through the steps of setting up signals. We will create an example where we send an email after a new user is created. Here's how you can set it up:
🛠️ Step 4.1: Create a Signal Receiver (Listener)
First, let's define the function that will handle the signal. This function is called a "receiver."
In your myapp/signals.py
file, create the following code:
# myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from django.conf import settings
from django.contrib.auth.models import User
# This is the receiver function
@receiver(post_save, sender=User)
def send_welcome_email(sender, instance, created, **kwargs):
if created: # Check if the user is newly created
send_mail(
'Welcome to our platform!',
'Thank you for signing up with us.',
settings.DEFAULT_FROM_EMAIL,
[instance.email],
fail_silently=False,
)
print(f"Sent welcome email to {instance.email}")
🛠️ Step 4.2: Connect the Signal to Your Application
In order for the signal to be executed, we need to connect the signal with the part of the app that will send it. This is typically done in the apps.py
file of the application.
In your myapp/apps.py
, add the following:
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals # Import signals when the app is ready
INSTALLED_APPS
🛠️ Step 4.3: Update Make sure your app is listed in the INSTALLED_APPS
section of your settings.py
:
# settings.py
INSTALLED_APPS = [
# Other apps
'myapp',
]
🧩 Step 5: Testing the Signal
Now that we’ve set up the signal, let's test it. Every time a new user is created, the send_welcome_email
function will be called, sending an email to the newly created user's email address.
To test it, go to the Django shell and create a new user:
python manage.py shell
Inside the shell, run the following commands:
from django.contrib.auth.models import User
# Create a new user
user = User.objects.create_user('john', '[email protected]', 'password123')
# Now, Django should send a welcome email to [email protected]
You should see a message in the console like this:
Sent welcome email to [email protected]
🧩 Step 6: Using Other Built-in Django Signals
Django provides many other useful signals, which you can use for various purposes. Here are a few examples:
pre_save
to modify data before saving
Example 1: Using The pre_save
signal is sent just before an object is saved to the database. You can use it to modify data before it’s saved.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(pre_save, sender=MyModel)
def modify_field_before_save(sender, instance, **kwargs):
if instance.some_field == 'some_value':
instance.some_field = 'modified_value'
post_delete
to do something after deleting an object
Example 2: Using The post_delete
signal is sent after an object is deleted. You might use this for clean-up tasks, like deleting related files.
from django.db.models.signals import post_delete
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(post_delete, sender=MyModel)
def delete_related_files(sender, instance, **kwargs):
# Imagine this model has an associated file that we want to delete
instance.related_file.delete()
🧩 Step 7: Custom Signals
While Django provides many built-in signals, sometimes you might need your own custom signals. Here’s how you can create and use custom signals in your Django application.
🛠️ Step 7.1: Create a Custom Signal
To create a custom signal, use the django.db.models.signals.Signal
class.
# myapp/signals.py
from django.db.models.signals import Signal
# Define a custom signal
user_logged_in = Signal()
🛠️ Step 7.2: Send the Custom Signal
You can send the custom signal using the send
method.
# myapp/views.py
from myapp.signals import user_logged_in
def user_login_view(request):
# User login logic
user_logged_in.send(sender=request.user.__class__, instance=request.user)
🛠️ Step 7.3: Connect a Receiver to the Custom Signal
Now, you can connect a receiver to the custom signal just like with Django’s built-in signals.
# myapp/signals.py
from django.db.models.signals import Signal
from django.dispatch import receiver
user_logged_in = Signal()
@receiver(user_logged_in)
def notify_admin_on_login(sender, instance, **kwargs):
print(f"Admin notified: {instance} logged in")
📝 Summary
Feature | What Happens |
---|---|
Django Signals | Mechanism to allow parts of your application to listen to events and react accordingly. |
Built-in Signals | Django provides signals for common events like saving and deleting objects. |
Custom Signals | You can create your own signals to handle specific application events. |
Receiver Functions | Functions that listen for signals and respond to them. |
Connecting Signals | Signals are connected to their receivers in the apps.py file of your Django app. |
Testing Signals | Signals are automatically triggered based on the events that occur in your app, such as creating a new object. |