Software Development
10 min
2025-10-12
Django Signals allow different parts of a Django application to communicate with each other when certain events occur without tight coupling. Think of them as "event listeners" that let you execute extra logic automatically when something happens, like saving a user, deleting an order, or updating a profile.
In simple terms, a signal is a notification sent by one part of your code that something has happened. Other parts of your app can "listen" for these notifications and perform specific actions in response. This makes your code modular, maintainable, and easier to extend.
Imagine your Django app as a company: when a new employee joins (a model instance is created), the HR department automatically sends a welcome email. Instead of calling HR from every department, Django Signals let HR listen for "employee_created" events and act automatically.
Signals help you keep your code loosely coupled. Instead of adding logic everywhere (like sending an email every time a user registers inside multiple views), you can centralize it in one place that automatically reacts to an event.
Django's signal system is based on the Observer Pattern. A signal is an instance of Django's Signal class, and functions that respond to it are called receivers.
When an event happens (for example, a model instance is saved), Django triggers a signal like post_save. Any receiver function connected to that signal gets executed automatically.
post_save, pre_delete)Django provides several built-in signals for models, requests, and the ORM:
pre_save: Sent before a model's save() is calledpost_save: Sent after a model's save() is calledpre_delete: Sent before a model's delete() is calledpost_delete: Sent after a model's delete() is calledm2m_changed: Sent when a ManyToMany relation changesrequest_started and request_finisheduser_logged_in, user_logged_out, user_login_failedLet's walk through a simple use case: sending a welcome email after a new user is created.
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
@receiver(post_save, sender=User)
def send_welcome_email(sender, instance, created, **kwargs):
if created:
print(f"Welcome {instance.username}! Your account has been created.")
# Here you could send an actual email
Explanation:
User instance is saved.You can connect signals manually using the connect() method:
from django.db.models.signals import post_save
from django.contrib.auth.models import User
def send_welcome_email(sender, instance, created, **kwargs):
if created:
print(f"Welcome {instance.username}!")
post_save.connect(send_welcome_email, sender=User)
Best practice is to keep signals in a dedicated signals.py file inside your app. Then import it in the apps.py file to ensure Django loads it when the app starts.
myapp/
├── apps.py
├── models.py
├── signals.py
└── __init__.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
import myapp.signals
This ensures signals are registered when Django starts up. Without this, your receivers won't get triggered.
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Now whenever a user is created, their profile is automatically created too — clean, simple, and decoupled.
You can also create your own signals. This is useful when you want to define custom application events.
from django.dispatch import Signal
order_completed = Signal()
# Sending the signal
order_completed.send(sender=None, order=order_instance)
# Listening to it
from django.dispatch import receiver
@receiver(order_completed)
def notify_user(sender, order, **kwargs):
print(f"Order {order.id} completed successfully!")
Let's summarize what happens when a signal fires:
post_save)apps.py, not models.pyTo prevent a signal from being registered multiple times, use dispatch_uid:
post_save.connect(send_welcome_email, sender=User, dispatch_uid="unique_welcome_email_signal")
Django Signals are an elegant way to make your applications more modular, decoupled, and reactive. Whether you're sending a welcome email, auto-creating profiles, or tracking logs, signals allow your app to behave intelligently in response to events — without unnecessary dependencies or code duplication. Once you master them, you can design cleaner, event-driven Django systems with ease.
Tags :
Django
Signals
EventDriven
PostSave
Architecture
BackendDevelopment