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)