When saving data to a database, there are unique use cases where the business requirement of an application may require some processing just before or after saving the data.
This means there should be a way to know when data is about to be saved or has just been saved in the database by the Django model method
One possible way is to override the
save() method on each model.
A neater and more efficient way is to use Django signals. These components work on the concept of senders and receivers. The sending component is usually the model, and the receiving component is usually the processing function that works on the data once it receives a notification that the data is just about to be saved or has just been saved.
This guide will explore how to use signals in Django as a method to pre-process and post-process data on its way to the database or just after saving to the database. It assumes you have at least beginner level knowledge in Django and a general understanding of the Django MVC, especially the models component. An introductory guide to Django can be found here, and a refresher guide on Django models can be found here.
Consider a use case where you have an ecommerce Django app with an orders and inventory model.
The business logic is such that before an order is saved, the inventory should be checked to ensure the item is in stock.
Also, after an order is saved, there should be a logic to send a notification that the order has been received. Below is a codeblock for the sample models.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
from django.db import models from django.contrib.auth.models import User class Inventory(models.Model): item = models.CharField(max_length=20) item_code = models.IntegerField() item_condition = models.CharField(max_length=50) quantity = models.IntegerField() def __str__(self): return self.item class Order(models.Model): ord_number = models.CharField(max_length=20) inventory_item = models.ForeignKey(Inventory) ordered_by = models.ForeignKey(User) quantity = models.IntegerField() def __str__(self): return self.ord_number
A pre-save signal is used in situations where logic has to be executed before data is saved to a database.
In your case, this involves determining whether an order is valid by checking whether the item exists in inventory.
The code block below defines a function to achieve this objective. The function can live within
1 2 3 4 5 6 7 8
from django.db.models.signals import pre_save def validate_order(sender, instance, **kwargs): if instance.quantity < instance.inventory_item.quantity: # order can be fulfilled instance.save() else: # write logic to reject save and give message why pre_save.connect(validate_order, sender=Order)
In the above code block, you have defined your business logic in the
and using the
pre_save function, and you have connected the receiver function to the sender, which is the
This is where you notify the user that the order has been successfully received.
post_save logic is just a normal function, the receiver function, but it's connected to a sender, which is the
The code block below demonstrates the sample receiver function as a post-save.
1 2 3 4 5 6 7
from django.db.models.signals import post_save from myapp.utils import send_notification def notify_user(sender, instance, **kwargs): send_notification(instance.ordered_by) post_save.connect(notify_user, sender=Order)
With this, once an order is successfully placed, the client will be notified via email or text, depending on what the business use case demands.
With knowledge of Django signals, you can now build robust web apps that can pre-process data during pre-save and post-save. This flexibility also allows you to build customized workflows that better address the needs of specific use cases.
To build on this guide, research more on signals and receivers, including how they work and if they can be overridden. Overridding gives the programmer the ability to customize pre-built functions. You might also bee interested in how a pre-save or post-save signal differentiates between a new record save and an update save for an existing record.