Sometimes after performing Django actions like entering, removing, and updating data into the Django Model, you want to run a certain function or make a modification.
At that moment, you need a Django signal. In this post, we’ll examine the Django signal and its type pre_save. Also, I have covered these points:–
- Prerequisite
- What is a pre_save signal in Django
- When to use pre_save signal in Django
- How does Django pre_save signal work using receiver decorator
- How does Django pre_save signal work using connect method
Prerequisite
Before proceeding to the pre_save signal. Django requires that you have some familiarity with signals. Let’s understand the signal.
When you want to execute a certain piece of code on the model or database before or after an event, you can use Django Signals. Signals are used to perform any action on modification or creation of a model instance. Basically, the signals are tools that support the relationship between events and actions.
For example, think about a scenario in which you have a Django food ordering project with an order and catalog model. The business logic is such that before an order is saved, the catalog should be checked to ensure the food is in stock. Also, after an order is saved, there should be a logic to send a notification that the order has been received.
As a result, we now understand what signal in Django is.
Read: Run Python Script in Django
Python Django pre_save signal
You have an overview of the signal. So, let’s discuss the pre_save signal now.
A pre_save signal is utilized in situations where logic has to be carried out before data is saved to a database. The syntax of the pre_save() signal function is as below.
django.db.models.signals.pre_save(sender, instance, raw, using, update_fields)
This function is sent at the beginning of a model save() method. And the following are the parameters.
Parameter | Description |
---|---|
sender | It specifies the model class. |
instance | The actual instance being saved. |
raw | If True, the model is saved exactly as presented. |
using | The alias being used in the database. |
update_fields | Fields passed to model that needs to be updated. If update_fields wasn’t supplied to save(), the default value is None. |
With this, we have learned an introduction to pre_save signal.
Read: Python Django concatenate string
Use pre_save signal in Django Python
After getting an overview of the pre_save signal. Now, let’s learn when to use Django pre_save signal.
- When you want to save a value produced by a function to a database, you often use the pre_save signal.
With this, we have learned the uses of the pre_save signal.
Python Django pre_save using receiver decorator
Let’s first learn about the receiver function, and after that, we’ll examine a Django pre_save signal example using the receiver decorator.
The receiver is the python function, routed to each signal. Every receiver that is connected receives a call when the signal sends a message. The syntax of the receiver function is as follows.
def receiver_func(sender, request, user, **kwargs)
The @receiver decorator is used to connect a receiver to a signal. The syntax of the receiver decorator is as follows:
@receiver(signals.pre_save, sender= <sender_name>
The parameters are as follows:
Parameter | Description |
---|---|
signal | Specifies the type of signal or a list of signals you want to use. Here pass as signals.pre_save. |
sender | Specifies the sender from which to receive signals. |
An example related to the @receiver decorator:
- Firstly, create a project in Django (named “eCartProject”) using the below-given command.
django-admin startproject eCartProject
- Create a Django app (named “eCartApp”), within the Django project, by typing the below command in the terminal.
python manage.py startapp eCartApp
- Add the “eCartApp” app to the installed app list located in the settings.py file.
- By default, Django has a urls.py file under the project. Django recommends mapping the newly created app “eCartApp” inside it.
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('eCartApp.urls')),
]
- Create the Django models that define the fields and behaviors of the “eCartApp” application. Open the models.py file which is in the Django app and add the below code.
from django.db import models
# Create your models here.
class Order(models.Model):
item = models.CharField(max_length=50)
price = models.FloatField()
delivery = models.FloatField()
quantity = models.IntegerField()
def __str__(self):
return self.item
Here, we create a model class “Order” with the following database fields.
- item: The name of the item.
- price: The price of the item.
- delivery: The delivery charges of the item.
- quantity: The amount of the food item in the stock available.
And to change the display name of the object in the Django model use def __str__(self). It will render the item name as we return the self.item.
- To register the model “Order” with the admin site, open the admin.py file and add the below-given code.
from django.contrib import admin
from .models import Order
# Register your models here.
class OrderAdmin(admin.ModelAdmin):
list_display = ("item", "price", "delivery", "quantity")
admin.site.register(Order, OrderAdmin)
- Create a urls.py file under the app directory.
from django.urls import path
urlpatterns = [
]
- Create a signals.py file under the app directory. And add the following code inside it.
Note: You can add signals to either model’s module or make a separate file for signals. But it is always a good idea to keep signals in a separate file as for signals you need to import some other modules which may have unintended consequences.
from django.db.models.signals import pre_save
from django.dispatch import receiver
from .models import Order
# pre_save signal using decorator
@receiver(signals.pre_save, sender=Order)
def check_delivery(sender, instance, **kwargs):
if not instance.delivery:
instance.delivery = 50
print("Item Purchased:",instance.item)
print("Price:",instance.price)
print("Delivery Charges:",instance.delivery)
else:
print("Item Purchased:",instance.item)
print("Price:",instance.price)
print("Delivery Charges:",instance.delivery)
First, we imported the pre_save signal and receiver, and order model, then we created a receiver function check_delivery() which will receive a pre_save signal. Then we are using a @receiver decorator to call the check_delivery() function on the delivery charges of the item.
The check_delivery() receiver function accepts the sender, instance, and kwargs parameters. We use the if–else statement which checks whether the delivery charges of the product are mentioned in the order.
If yes, the item name, price, and delivery charges are printed on the terminal using the print() function[ i.e else part executes]. And if no, the item name, price, and assigned delivery charges are printed on the terminal using the print() function [i.e if part executes].
- As the Django Community advises putting signals in a separate file, you also have to put it in an apps.py file. Add the below-given code.
from django.apps import AppConfig
class EcartappConfig(AppConfig):
name = 'eCartApp'
def ready(self):
import eCartApp.signals
Whenever Django loads the project and its apps, the ready() method is called on the Appconfig instance of each app.
And here you use this method to tell Django about the signal handlers in the app so that Django knows about their existence.
- Open the __init__.py file inside the app directory and add the given code to it.
default_app_config = 'eCartApp.apps.EcartappConfig'
As you have created the custom AppConfig class in an app you have to inform Django about its existence. For this, you have to set the variable default_app_config in the __init__.py file for that app.
Bonus: If you don’t want to add a custom AppConfig class in __init__.py file. Add the custom AppConfig class name in the settings.py file instead of the app name as shown below.
- Use the command shown below to generate a migration for the specified model. A migration file will be created and placed within the migration folder.
python manage.py makemigrations
- To reflect the database, first, create a migration and then migrate it. The migrate command is listed below.
python manage.py migrate
- Enter the following command in the terminal to create the superuser.
python manage.py createsuperuser
- To add the records in the “Order” model open the admin application and add it.
- Now, let’s see the working of the pre_save signal. So, if you order an item having delivery charges. The following is the output on the screen.
- If you order an item having zero delivery charges. It will call the pre_signal and automatically change it to 50. The following is the output on the screen.
This is how you use Django pre_save signal work using the receiver decorator.
Read: Python Django app upload files
Python Django pre_save using connect method
You have learned to use the pre_save signal type using the receiver decorator. So, now we’ll move ahead and learn another way i.e connect method to use the pre_save signal.
To receive a signal, register a receiver function using the connect() method. When the signal is sent, the receiver function is activated. Each receiver function for the signal is called once, in the order that it was registered.
The following is the syntax of the connect() method.
pre_save.connect(recevier_func, sender=None, weak=True, dispatch_uid=None)
The parameters are as follows:
Parameter | Description |
---|---|
recevier_func | The callback function will be connected to the signal. |
sender | Defines a specific sender from which to receive signals. |
weak | If the receiver is a local function, it may be garbage collected. To prevent this, set its bool value to False. |
dispatch_uid | A unique identifier for a signal receiver in cases where duplicate signals may be sent. |
An example related to the connect() method:
- Firstly, create a project in Django (named “Hotel”) using the below-given command.
django-admin startproject Hotel
- Create a Django app (named “FoodCourt”), within the Django project, by typing the below command in the terminal.
python manage.py startapp FoodCourt
- Add the “FoodCourt” app to the installed app list located in the settings.py file.
- By default, Django has a urls.py file under the project. Django recommends mapping the newly created app “FoodCourt” inside it.
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('FoodCourt.urls')),
]
- Create the Django models that define the fields and behaviors of the “FoodCourt” application. Open the models.py file which is in the Django app and add the below code.
from django.db import models
# Create your models here.
class Catalog(models.Model):
item = models.CharField(max_length=50)
item_code = models.IntegerField()
quantity = models.IntegerField()
def __str__(self):
return self.item
class Order(models.Model):
order_number = models.CharField(max_length=20)
catalog_item = models.ForeignKey(Catalog, on_delete=models.CASCADE)
quantity = models.IntegerField()
def __str__(self):
return self.order_number
Here, we first create a model class “Catalog” which has the following database fields.
- item: The name of the food item.
- item_code: The code of the food item.
- quantity: The amount of the food item in the stock available.
And to change the display name of the object in the Django model use def __str__(self). It will render the item name as we return the self.item.
Then we create a model class “Order” which has the following database fields.
- order_number: The order number of the order.
- catalog_item: It is a foreign Key that is used for mapping the relationships between tables in relational databases.
- quantity: The amount of the food item ordered.
And to change the display name of the object in the Django model use def __str__(self). It will render the order number as we return the self.order_number.
- To register the models “Catalog” and “Order” with the admin site, open the admin.py file and add the below-given code.
from django.contrib import admin
from .models import Catalog,Order
# Register your models here.
class CatalogAdmin(admin.ModelAdmin):
list_display = ("item", "item_code", "quantity")
class OrderAdmin(admin.ModelAdmin):
list_display = ("order_number", "catalog_item", "quantity")
admin.site.register(Catalog, CatalogAdmin)
admin.site.register(Order, OrderAdmin)
- Create a urls.py file under the app directory.
from django.urls import path
urlpatterns = [
]
- In the above example, you have learned to create a signal in the signals.py file. So, as you know we also have the provision to add the signal code inside the models.py file. Add the below-given code in the models.py file to use the pre_save signal with the connect() method.
from django.db.models.signals import pre_save
# pre_save signal with connect() method
def verify_order(sender, instance, **kwargs):
if instance.quantity < instance.catalog_item.quantity:
print('Your Order Number:', instance.order_number)
print('Orderd Successfully')
else:
print('Your Order Number:',instance.order_number)
print('Item Name:', instance.catalog_item)
print('Out of stock')
pre_save.connect(verify_order, sender=Order)
First, we imported the pre_save signal, then we created a receiver function verify_order() which will receive a pre_save signal. Then we are using a connect() method to call the verify_order() function on the order of the food.
The verify_order() receiver function accepts the sender, instance, and kwargs parameters. We use the if–else statement which checks whether the quantity of food ordered is less than the quantity of the food in the catalog.
If yes, the order number and message are printed on the terminal using the print() function. And if no, the order number, item name, and message are printed on the terminal using the print() function.
- To create a migration for the model, use the below-given command. Inside the migration folder, a migration file will be created.
python manage.py makemigrations
- Create a migration and then migrate it to reflect the database. Below is the migrate command.
python manage.py migrate
- To create the super user type the following command in the terminal.
python manage.py createsuperuser
- To add the records in the “Catalog” model open the admin application and add it.
- Now, let’s see the working of the pre_save signal. So, if you order an item having a quantity less than the quantity of the food in the catalog. The following is the output on the screen.
- If you order an item having a quantity greater than the quantity of the food in the catalog. The following is the output on the screen.
This is how you use Django pre_save signal work using connect method.
Read: Union operation on models Django
Conclusion
With this, we know Django signals and are also able to create reliable web applications that can pre-process data during pre_save. With this flexibility, you can also create unique workflows that more effectively meet the demands of particular use cases.
Additionally, we have also covered the following topics.
- Prerequisite
- What is a pre_save signal in Django
- When to use pre_save signal in Django
- How does Django pre_save signal work using receiver decorator
- How does Django pre_save signal work using connect method
Also, take a look at some more Python Django tutorials.
- Login system in Python Django
- Python Django get enum choices
- Outputting Python to html Django
- Python Django random number
- Simple Contact Form for website in Python Django
Python is one of the most popular languages in the United States of America. I have been working with Python for a long time and I have expertise in working with various libraries on Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… I have experience in working with various clients in countries like United States, Canada, United Kingdom, Australia, New Zealand, etc. Check out my profile.