How to Use Django pre_save and post_save Signals Effectively
By khoanc, at: March 15, 2025, 8:01 p.m.
Estimated Reading Time: __READING_TIME__ minutes


Django signals are a powerful tool for decoupling logic in your application. The pre_save
and post_save
signals, in particular, allow developers to execute custom actions before or after saving a model instance to the database. These signals are ideal for automating tasks, reducing manual intervention, and maintaining cleaner code.
In this blog post, we’ll explore how to use these signals effectively by presenting a real-world case study: managing warehouse inventory and sending low stock notifications.
Real-World Case Study: Smart Warehouse Inventory Management
The Scenario
Imagine you’re building an inventory management system for a warehouse. Each time a product is added or updated:
- The total product count in the warehouse must be updated automatically.
- If the product quantity falls below a threshold, the warehouse owner must be notified via email to prevent stockouts.
This workflow ensures the warehouse runs efficiently, eliminates manual tracking, and prevents inventory shortages.
Implementation
Step 1: Define the Models
We’ll start by creating two models:
- Warehouse: Stores the total count of all products.
- Product: Represents individual products linked to a warehouse.
from django.db import models
class Warehouse(models.Model):
name = models.CharField(max_length=255)
total_products = models.PositiveIntegerField(default=0)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=255)
quantity = models.PositiveIntegerField(default=0)
warehouse = models.ForeignKey(Warehouse, on_delete=models.CASCADE, related_name="products")
def __str__(self):
return self.name
Step 2: Connect Signals for Automation
We’ll use two signals:
pre_save
to adjust the warehouse’s total product count before saving.
post_save
to send an email notification if the product quantity is low.
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
from django.core.mail import send_mail
from .models import Product, Warehouse
@receiver(pre_save, sender=Product)
def update_total_products(sender, instance, **kwargs):
if instance.pk:
# Fetch the old quantity from the database
old_quantity = Product.objects.get(pk=instance.pk).quantity
difference = instance.quantity - old_quantity
else:
difference = instance.quantity
# Update the warehouse's total product count
instance.warehouse.total_products += difference
instance.warehouse.save()
@receiver(post_save, sender=Product)
def send_low_stock_notification(sender, instance, **kwargs):
low_stock_threshold = 10 # Low stock threshold
if instance.quantity < low_stock_threshold:
# Send an email notification to the owner
send_mail(
subject=f"Low Stock Alert: {instance.name}",
message=f"The product '{instance.name}' is running low with only {instance.quantity} items remaining. Please restock soon.",
from_email="[email protected]",
recipient_list=["[email protected]"], # Replace with the warehouse owner's email
)
Step 3: Register the Signals
Ensure the signals are registered when the app is ready by importing them in the apps.py
file.
# apps.py
from django.apps import AppConfig
class InventoryConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "inventory"
def ready(self):
import inventory.signals # Import signals
Testing the System
Add a New Product
Create a new product and verify the warehouse's total product count updates automatically.
warehouse = Warehouse.objects.create(name="Main Warehouse")
product = Product.objects.create(name="Widget", quantity=20, warehouse=warehouse)
print(warehouse.total_products) # Output: 20
Update Product Quantity
Update the product quantity and ensure the warehouse total reflects the change.
product.quantity = 35
product.save()
print(warehouse.total_products) # Output: 35
Low Stock Notification
Reduce the product quantity below the threshold and check for an email notification.
product.quantity = 5
product.save()
# Email sent: "The product 'Widget' is running low with only 5 items remaining."
Benefits of This Approach
-
Automation
Warehouse totals and low stock alerts are handled automatically, eliminating the need for manual updates.
-
Proactive Notifications
Owners are alerted about low stock before it becomes a critical issue, ensuring smooth operations.
-
Decoupled Logic
Signals keep the inventory logic separate from the core application, making the system more modular and maintainable.
Best Practices for Using Django Signals
-
Avoid Complex Logic
Keep signal handlers simple. For heavy tasks like sending emails, consider using a background task queue like Celery.
-
Minimize Signal Overhead
Ensure signals don’t perform unnecessary database queries or slow down request handling.
-
Test Signal Behavior
Test thoroughly to ensure signals trigger as expected and don’t interfere with other parts of the application.
Conclusion
Django’s pre_save
and post_save
signals are excellent tools for automating workflows and keeping your application logic clean. In this case study, we’ve shown how signals can streamline inventory management by automatically updating warehouse totals and notifying owners of low stock.
By implementing these techniques, you can build smarter, more efficient systems that save time and prevent costly errors.
Ready to optimize your Django app? Start leveraging signals today!