How to use Python Django pre_save

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.

ParameterDescription
senderIt specifies the model class.
instanceThe actual instance being saved.
rawIf True, the model is saved exactly as presented.
usingThe alias being used in the database.
update_fieldsFields 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:

ParameterDescription
signalSpecifies the type of signal or a list of signals you want to use. Here pass as signals.pre_save.
senderSpecifies 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.
django pre_save using receiver
Settings.py in Django Project
  • 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.
READ:  PyTorch Tensor to Numpy

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 ifelse 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.

READ:  Pandas Get Index of Row in Python [6 Ways]

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.

pre save django using receiver decorator
settings.py
  • 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.
django pre save using decorator
Order Model in Admin Site
  • 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.
python django pre save example
An item with delivery charges
python django pre save signals
pre_save signal output
  • 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.
django signal using pre save
An item with zero delivery
python django pre_save
pre_save signal example

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:

ParameterDescription
recevier_funcThe callback function will be connected to the signal.
senderDefines a specific sender from which to receive signals.
weakIf the receiver is a local function, it may be garbage collected. To prevent this, set its bool value to False.
dispatch_uidA 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.
pre save app
settings.py
  • 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.
READ:  Put legend outside plot matplotlib

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 ifelse 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.
django pre save
Catalog Model in Admin Site
  • 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.
python django pre save
An item with a quantity less than the quantity of the food
django pre save using connect
Django pre_save using connect
  • 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.
django pre save example
Item order greater than the item in catalog
django pre save signal
Django pre_save signal

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.