π Django Signals: The Secret Messengers of Your App
Imagine you have a house with many rooms. When the doorbell rings, everyone in every room hears it instantlyβwithout you running to tell each person. Thatβs Django Signals!
π What Are Signals? (Signals Overview)
Think of signals like a magical announcement system in your Django app.
The Pizza Shop Story π
Imagine you own a pizza shop:
- When a new order comes in, you need to:
- Tell the kitchen to start cooking
- Update the order board
- Send a confirmation to the customer
Without signals: Youβd have to manually call each department every single time.
With signals: The order system automatically broadcasts βNew Order!β and everyone who needs to knowβ¦ just knows!
# This is the magic! πͺ
# When something happens β others react automatically
Why Signals Are Amazing
| Without Signals π« | With Signals π |
|---|---|
| Code is tangled together | Code is clean and separate |
| Hard to add new features | Easy to add new reactions |
| One change breaks everything | Changes are safe |
The Three Parts of a Signal
graph TD A["π‘ SIGNAL"] --> B["Something happened!"] B --> C["π― SENDER"] C --> D["Who sent it?"] D --> E["π RECEIVER"] E --> F["Who listens and reacts?"]
Simple Example:
# Signal = "Doorbell rang!"
# Sender = The doorbell
# Receiver = You, running to answer
ποΈ Model Signals: Your Databaseβs Announcements
Model Signals are special signals that fire when something happens to your database records.
The Library Book Story π
Think of your database like a library:
- pre_save = βIβm about to put this book on the shelf!β
- post_save = βI just put this book on the shelf!β
- pre_delete = βIβm about to remove this book!β
- post_delete = βI just removed this book!β
The Main Model Signals
| Signal | When It Fires | Real Example |
|---|---|---|
pre_save |
Before saving | Check if username is valid |
post_save |
After saving | Send welcome email to new user |
pre_delete |
Before deleting | Backup important data |
post_delete |
After deleting | Clean up related files |
See It In Action
from django.db.models.signals import post_save
from django.contrib.auth.models import User
# When a new user is created...
# This function runs automatically!
def welcome_new_user(sender, instance, created, **kwargs):
if created: # Only for NEW users
print(f"Welcome, {instance.username}! π")
# Connect it (we'll learn this next!)
post_save.connect(welcome_new_user, sender=User)
What happens:
- Someone creates a new User β
- Django saves it to database β
post_savesignal fires π‘- Your function runs automatically π
The created Parameter Magic
def my_handler(sender, instance, created, **kwargs):
if created:
# This is a BRAND NEW record
print("New baby was born! πΆ")
else:
# This is an UPDATE to existing record
print("Someone grew taller! π")
π Connecting Signal Receivers
Now letβs learn two ways to tell Django: βHey, when THIS happens, run THAT function!β
Method 1: The connect() Way
Think of it like plugging in a cable:
from django.db.models.signals import post_save
from myapp.models import Order
def notify_kitchen(sender, instance, **kwargs):
print(f"Kitchen: Start making order #{instance.id}!")
# Plug it in! π
post_save.connect(notify_kitchen, sender=Order)
Method 2: The @receiver Decorator (Easier!)
Think of it like wearing a name tag that says βI listen to this!β
from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import Order
@receiver(post_save, sender=Order)
def notify_kitchen(sender, instance, **kwargs):
print(f"Kitchen: Start making order #{instance.id}!")
Both do the same thing! The decorator is cleaner.
Where to Put Your Receivers? π
Best Practice: Create a signals.py file in your app:
myapp/
βββ __init__.py
βββ models.py
βββ signals.py β Put receivers here!
βββ apps.py β Connect them here!
In apps.py:
from django.apps import AppConfig
class MyappConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals # Load signals!
Quick Connection Checklist
graph TD A["1. Create receiver function"] --> B["2. Add @receiver decorator"] B --> C["3. Put in signals.py"] C --> D["4. Import in apps.py ready"] D --> E["β Signal connected!"]
β¨ Custom Signals: Create Your Own Announcements!
Sometimes Djangoβs built-in signals arenβt enough. You want to broadcast YOUR own messages!
The School Bell Story π
Your school has different bells:
- Morning bell = Class starts
- Lunch bell = Time to eat
- Fire alarm = Everyone evacuate!
Django gives you built-in bells. Custom signals let you create YOUR OWN bells!
Creating a Custom Signal
# signals.py
from django.dispatch import Signal
# Create your own signal! π
order_completed = Signal()
# What data will it carry?
# You decide when you send it!
Sending Your Custom Signal
from myapp.signals import order_completed
def finish_order(order):
# Do the work...
order.status = 'completed'
order.save()
# π‘ Broadcast your signal!
order_completed.send(
sender=order.__class__,
order=order,
total=order.total
)
Receiving Your Custom Signal
from django.dispatch import receiver
from myapp.signals import order_completed
@receiver(order_completed)
def send_receipt(sender, order, total, **kwargs):
print(f"Sending receipt for ${total}!")
@receiver(order_completed)
def update_inventory(sender, order, **kwargs):
print("Updating stock levels...")
@receiver(order_completed)
def reward_points(sender, order, **kwargs):
print("Adding loyalty points! β")
One signal β Many receivers! Thatβs the power!
Complete Custom Signal Example
# Step 1: Define signal
pizza_ready = Signal()
# Step 2: Create receivers
@receiver(pizza_ready)
def notify_customer(sender, pizza, **kwargs):
print(f"Your {pizza.name} is ready! π")
@receiver(pizza_ready)
def ring_bell(sender, **kwargs):
print("DING! π")
# Step 3: Send when ready
def bake_pizza(pizza):
# ... baking logic ...
pizza_ready.send(sender=Pizza, pizza=pizza)
π― Summary: Your Signal Superpowers
graph TD A["π DJANGO SIGNALS"] --> B["π‘ Overview"] A --> C["ποΈ Model Signals"] A --> D["π Connecting"] A --> E["β¨ Custom"] B --> B1["Sender β Signal β Receiver"] C --> C1["pre_save, post_save"] C --> C2["pre_delete, post_delete"] D --> D1["connect method"] D --> D2["@receiver decorator"] E --> E1["Signal class"] E --> E2["send method"]
Quick Reference
| Want To⦠| Use This |
|---|---|
| React before save | pre_save |
| React after save | post_save |
| React before delete | pre_delete |
| React after delete | post_delete |
| Create own signal | Signal() |
| Connect easily | @receiver |
| Send your signal | .send() |
π You Did It!
You now understand Django Signals! Remember:
Signals = Automatic announcements
Something happens β Signal fires β Receivers react
No manual calling needed! π
Go forth and build amazing, decoupled, clean Django applications!
