Adding dynamic control to signals with django-signalcontrol

Django signals provides a great means to launch an event in a given scenario, such as sending a message when a model instance is saved. However, there are situations that might necessitate the temporarily disabling of a signal, such as an issue with a downstream dependancy. The django-signalcontrol package was created with this in mind. With django-signalcontrol, signals with signal_control applied are automatically scanned when django starts, and can be disabled or enabled in the django admin interface, individually or in bulk.

In this post we’ll look at how django-signalcontrol works.

First lets get django-signalcontrol installed; we can do this with a simple pip install:

pip install django-signalcontrol

Next, we need to add signalcontrol to the INSTALL_APPS in settings.py file:

INSTALLED_APPS = [

‘signalcontrol’,
]

Lastly, run migrations:

python ./manage.py migrate signalcontrol

To see django-signalcontrol in action, let’s create a django app.

First let’s prep our app. For this example, I have a django project called demo_signalcontrol and an app called app1.

add app1 to INSTALL_APPS:
INSTALLED_APPS = [

‘signalcontrol’,
‘app1’,
]

Next let’s add a few simple models in our models.py to test signalcontrol:

from django.db import models# Create your models here.
class MyModelOne(models.Model):
name = models.CharField(max_length=16)
class MyModelTwo(models.Model):
name = models.CharField(max_length=16)
class MyModelThree(models.Model):
name = models.CharField(max_length=16)

We’ll make and apply migrations, and create a superuser as we’ll need a user to log in to the admin page:

./manage.py makemigrations app1
./manage.py migrate
./manage.py createsuperuser

To make signals available when django starts, we’ll add a ready function in the AppConfig in the apps.py file. The apps.py will look like this:

from django.apps import AppConfig

class App1Config(AppConfig):
name = 'app1'

def ready(self):
import app1.signals

and in the __init__.py we add a single line:

default_app_config = ‘app1.apps.App1Config’

Now we’re ready to make some signals to show how django-signalcontrol works. We’ll start by creating a signals.py file and add a simple post_save signal for each model that’ll print out a message:

from django.db.models.signals import (post_save)
from django.dispatch import receiver
from .models import (MyModelOne, MyModelTwo, MyModelThree)
from signalcontrol.decorators import signal_control
@receiver(post_save, sender=MyModelOne)
@signal_control
def msg_my_model_one(sender, instance, created, **kwargs):
print(“you just saved an instance of MyModelOne”)
@receiver(post_save, sender=MyModelTwo)
@signal_control
def msg_my_model_two(sender, instance, created, **kwargs):
print(“you just saved an instance of MyModelTwo”)
@receiver(post_save, sender=MyModelThree)
@signal_control
def msg_my_model_three(sender, instance, created, **kwargs):
print(“you just saved an instance of MyModelThree”)

Start the demo server and and you’ll see a message indicating each signal has been registered:

INFO: registering msg_my_model_one in app1 with SignalControl
INFO: registering msg_my_model_two in app1 with SignalControl
INFO: registering msg_my_model_three in app1 with SignalControl

Navigate to the django admin page and you’ll a listing for the Signal Control app. In it, there will be a listing for registered signals.

It’s time to test controlling our signals. To do this, let’s launch a djangoshell. Next, import one of our models, and create an entry. With the post_save signal enabled, we see the expected message printed out.

>>> from app1.models import MyModelOne
>>> m = MyModelOne.objects.create(name=’test’)
you just saved an instance of MyModelOne

We can execute the save method on our model instance to see the signal working

>>> m.save()
you just saved an instance of MyModelOne
>>>

back in the admin page, we can set the signal to disable. In the django shell, we can call the save method a few more times and see the message in the signal is no longer being displayed.

>>> m.save()
>>> m.save()
>>>

After re-enabling the signal in the admin panel, we now have the post_save message displaying again.

>>> m.save()
you just saved an instance of MyModelOne
>>>

For full details on django-signalcontrol, see the official documentation here: https://django-signalcontrol.readthedocs.io/en/latest/

Automation Engineer & Django Fan