Planet Python

Understanding Django signals

In many cases when there is a modification in a model’s instance we need execute some action. Django provides us an elegant way to handle with these situations. The signals are utilities that allow us to associate events with actions. We can develop a function that will run when a signal calls it.

Some of the most used models’ signals are the following:

  • pre_save/post_save: This signal is thrown before/after the method save().

  • pre_delete/post_delete: Before after delete a model’s instance (method delete()) this signal is thrown.

  • pre_init/post_init: This signal works before/after instantiating a model (__init__() method)

In this post we will explain the django signals with examples of models’ signals, but the signals don’t only work for models. You can check the complete list of signals in this link.

Connecting signals

You can associate your functions to signals in two different ways:

Decorator: With the @receiver decorator, we can link a signal with a function:

from django.db.models.signals import post_save
from django.dispatch import receiver
from someapp.models import MyModel

@receiver(post_save, sender=MyModel)
def my_function_post_save(sender, **kwargs):
   # do the action…

Every time that a MyModel’s instance ends to run its save() method, the my_function_post_save will start to work.

The other way is to connect the signal with the function:

post_save.connect(my_function_post_save, sender=MyModel)

 How can we use a signal-function with several senders?

Let’s suppose we have a base class model and we want to associate a signal with a function for all subclasses of our base class. We can do this using both previous ways:

You can learn more about django signals in the official documentation.

@receiver(post_save, sender=MyBaseClassModel.__subclasses__())
for sub_class in MyBaseClassModel.__subclasses__():
   post_save.connect(my_function_post_save, sender=sub_class)
  1.  We can use a list as sender argument for @receiver:del.  ​​​​​​ 
  2. With the other way we can use a for loop:

Where should I write my signals?

Since Django 1.7, the official documentation suggests us to write our signals functions in a different file (i.e. and connect them with the signals in the file of our app. Here we have an example:

def my_signal_function(sender, **kwargs):
   print("printing a message...")
from django.apps import AppConfig
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _
from .signals import my_signal_function

class MyAPPConfig(AppConfig):
   name = 'myapp'
   verbose_name = _('My App')

   def ready(self):
       myccl = self.get_model('MyCustomClass')
       post_save.connect(my_signal_function, sender=myccl, dispatch_uid="my_unique_identifier")

Sometimes, the signal is called more than once. For that reason, we use an unique string with the argument dispatch_uid in the connect function.

What is it used for?

Well, we already know how to configure our signals and signals function, but when must we use it? Here we show you some powerful examples of django signals:

  • We can have a model that stores a file on disk, i.e. DrivingLicenseModel. This model will have 3 attributes: name, due_date and photography. The photography will be an image that we will store in the disk.
    We can use a pre_delete signal for removing the image of the disk before deleting our DrivingLicenseModel’s instance.

  • We have a List of movies per user in our website (ListMoviesModel) which has an attribute for the last time that a user modifies it. A user can add a movie (ListMoviesEntryModel) to the previous list.
    Every time a user adds or modifies a singular movie, we can update the last_modification attribute of the ListMoviesModel’s instance for that user if we establish a post_save signal for the ListMoviesEntryModel model.

You can learn more about django signals in the official documentation.

About the author

Let’s have a coffee and talk about your project


Let’s have a coffee and talk about your project


We use cookies to ensure you get the best experience on our website. More info.