DEV Community

Cover image for 🔔 Django Signals: Supercharging Your App with Event-Driven Architecture
Divesh Kumar
Divesh Kumar

Posted on

🔔 Django Signals: Supercharging Your App with Event-Driven Architecture

When building a Django application, you often need to perform actions whenever something specific happens — for example, sending a welcome email after a user registers.

Instead of writing extra logic inside your views or models, Django gives us a powerful feature: Signals.

In this post, we’ll break down what signals are, why you need them, and a real-world example with code.

🚀 What are Django Signals?

Django Signals allow decoupled applications to get notified when certain actions occur.

Think of them like a notification system inside Django.
• Event happens → Signal is fired
• Listener catches it → Executes extra logic

Examples:
• A user logs in → Update last login timestamp
• A new order is created → Send invoice email
• A profile is saved → Resize uploaded avatar

🛠️ Common Built-in Signals

Django comes with some built-in signals:
• pre_save / post_save → before/after saving a model
• pre_delete / post_delete → before/after deleting a model
• m2m_changed → when a ManyToMany relation is updated
• request_started / request_finished → when a request starts/ends
• user_logged_in / user_logged_out → authentication-related signals

You can also create custom signals.

🧑‍💻 Example: Auto-Creating a Profile When a User Registers

Instead of writing profile creation logic inside the signup view, let’s use signals.

Step 1: Create a Profile model

# app/models.py from django.db import models from django.contrib.auth.models import User class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) bio = models.TextField(blank=True) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f"{self.user.username}'s profile" 
Enter fullscreen mode Exit fullscreen mode

Step 2: Write a Signal Receiver

# app/signals.py from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth.models import User from .models import Profile @receiver(post_save, sender=User) def create_user_profile(sender, instance, created, **kwargs): if created: Profile.objects.create(user=instance) 
Enter fullscreen mode Exit fullscreen mode

Here’s what happens:
• When a new User is created → post_save signal is fired.
• Our create_user_profile function catches it.
• A Profile is automatically created for the new user.

Step 3: Connect the Signal in apps.py

# app/apps.py from django.apps import AppConfig class AppConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'app' def ready(self): import app.signals 
Enter fullscreen mode Exit fullscreen mode

Now, whenever a new user registers, Django automatically creates a profile without extra code in the view.

⚡ Why Use Signals?
✅ Keeps code clean & decoupled
✅ Avoids duplicate logic across views
✅ Makes your app event-driven

⚠️ When NOT to Use Signals?
• When the logic is very specific to one place (better to keep it in the view).
• When debugging complex chains (signals can make code harder to trace).
• If overused → can create “hidden logic” that’s difficult for teams to maintain.

🎯 Final Thoughts
Django Signals are a game-changer for clean and modular code. Use them wisely to automate side-effects like profile creation, notifications, and logging.

👉 If you found this helpful, drop a ❤️ or share your thoughts in the comments.

Top comments (0)