How to Use and Create Django Signals

How to Use and Create Django Signals

Django Signals helps to allow decoupled applications to get notified when actions or events occur. We can think of Django Signals as triggers. Django Signals are useful when we need to invoke a piece of code at certain events. The benefit of these signals is at the model level where we need to write action once.

Django provides many built-in signals dispatchers like – pre_save, post_save, pre_delete, post_delete, m2m_changed, request_started and request_finished.

Let’s say you want to check an email id before entering it in the model, but there are several places in your codebase that this model can be created or updated. You can do that thing using Signals, by just attaching some code that will be executed every time with the model’s save method.

Video Tutorial of Django Signals

When to use Django Signals?

You can use Django Signals when you want to run a specific code before or after the event on the model/database. For example when you update user then it should also update the profile model instance using the post_save method.

Simple Django Signal Example

# models.py

from django.db import models
from django.db.models import signals
from django.dispatch import receiver  

class Product(models.Model):
    name = models.CharField(max_length=16)
    description = models.CharField(max_length=32, blank=True)  

@receiver(signals.post_save, sender=Product) 
def create_product(sender, instance, created, **kwargs):
    print("Save method is called")

So in the above example, when save method of Product model is called the post_save signal notification is invoked. Instead of print in create_product method, you can write logic and code.

Now we will take pre_save method example by add default description if it is not added-

# models.py

from django.db import models
from django.db.models import signals
from django.dispatch import receiver 

class Product(models.Model):
    name = models.CharField(max_length=16)
    description = models.CharField(max_length=32, blank=True) 

@receiver(signals.pre_save, sender=Product)
def check_product_description(sender, instance, **kwargs):
    if not instance.description:
        instance.description = 'This is Default Description' 

Note – We are using pre_save signal with receiver decorator in the above example.

Where should signal handlers live in a Django project?

We can put our signals in the Models module. But for signals we need to import some other modules which may have side-effects. So, it is a good idea to avoid putting it inside the models module or in the application root module.

Django Community recommends creating a separate file for signals and put it in an app confile file. See how I do it in bellow code.

Note– I am considering my app named product which is registered in INSTALLED_APPS.

product/signals.py

from django.db.models import signals
from django.dispatch import receiver
from .models import Product

# pre_save method signal
@receiver(signals.pre_save, sender=Product)
def check_product_description(sender, instance, **kwargs):
    if not instance.description:
        instance.description = 'This is Default Description'
    
# post_save method
@receiver(signals.post_save, sender=Product) 
def create_product(sender, instance, created, **kwargs):
    print("Save method is called") 

product/apps.py

from django.apps import AppConfig

from django.utils.translation import ugettext_lazy as _

class ProductConfig(AppConfig):
    name = 'product'

    def ready(self):
        import product.signals  # noqa 

product/__init__.py

default_app_config = 'product.apps.ProductConfig' 

Django Built-In Signals

Django provide lots of built-in signals like model and request/response signals. You can also write your own signals. These signals are an instance of Django signal dispatcher.

Model Signals

django.db.models.signals.pre_save & django.db.models.signals.post_save

pre_save and post_save acts before and after a model’s save() method is called.

django.db.models.signals.pre_delete & django.db.models.signals.post_delete

pre_delete and post_delete acts before and after a model’s delete() method or queryset’s delete() method is called.

django.db.models.signals.m2m_changed

m2m_changed acts on ManyToManyField on a model is changed.

Request / Response Signals

django.core.signals.request_started & django.core.signals.request_finished

Sent when Django starts or finishes an HTTP request.

Want to learn more about built-in Django signals – click here

You can also find this project code on GitHub

Share