Django Signal

The Django Signals is a strategy to allow decoupled applications to get notified when certain events occur.

There are two key elements in the signals machinery: the senders and the receivers

As the name suggests, the sender is the one responsible to dispatch a signal, and the receiver is the one who will receive this signal and then do something.

  • A receiver must be a function or an instance method which is to receive signals.
  • A sender must either be a Python object, or None to receive events from any sender.

Types of Signal

Model signals

  • The django.db.models.signals module defines a set of signals sent by the model system.
  • pre_save, post_save,
  • pre_delete, post_delete
  • m2m_changed
  • request_started, request_finished

Management signals

  • Signals sent by django-admin.

Request/response signals

  • Signals sent by the core framework when processing a HTTP request.

Test signals

  • Signals only sent when running tests.

Database Wrappers

  • Signals sent by the database wrapper when a database connection is initiated.

Working

  • To receive a signal, you need to register a receiver function that gets called when the signal is sent by using the Signal.connect() method.
  • There are two ways you can connect a receiver to a signal
    • Signal.connect()
    • @receiver()
      • It internally use Signal.connect()

Without signal.py

  • blog/models.py
    • Here sender & receiver are at same place
    • “sender=Fruit” —> will send signal only for Fruit model
      • If not specified, will send signal for all models
# Way1 - signal.connect()
from django.db.models.signals import post_save
from django.contrib.auth.models import User

# receiver
def save_post(sender, instance, **kwargs):
    print('something')

# sender
post_save.connect(save_post, sender=User)
  • OR
# Way2 - @receiver()
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User

@receiver(post_save, sender=User)
def save_post(sender, instance, **kwargs):
    print('something')

With signal.py

# signal.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

# Way1 - signal.connect()
def save_pre(sender, instance, **kwargs):
	print('Pre save --- something')

pre_save.connect(save_pre, sender=Fruit)


# Way2 - @receiver()
@receiver(post_save, sender=User)
def save_post(sender, instance, **kwargs):
    print('Post save --- DO something')
  • NOTE: Below code tells project about signal.py
# apps.py
from django.apps import AppConfig
class BlogConfig(AppConfig):
    name = 'blog'

    def ready(self):
    	import blog.signals

Custom signals

  • Your applications can take advantage of the signal infrastructure and provide its own signals.
  • Creating custom signal using dispatch.Signal
  • All signals are django.dispatch.Signal instances.
import django.dispatch

# Create Signal
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])


class PizzaStore:
    ...

    def send_pizza(self, toppings, size):
    	# Send Signal
        pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
        ...

# Receive Signal
@receiver(pizza_done)
def pizza_done_receiver(sender, **kwargs):
	print(kwargs)

signals.py

from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

Reference