XOR Media

Django Startup Signal

If you’ve spent some time with Django chances are you’ve run across its signals system. It is most commonly used to provide hooks in to the model setup and CRUD processes, but there are many other uses of the facility throughout the code including auth, db, and the request life-cycle. What it doesn’t offer out of the box is a signal that’s called during startup after your models have been loaded.

There are many potential uses of a startup signal, but for the purposes of this post we’ll use this signal for something that’s invaluable when debugging production applications. We’ll use it to log the application’s current version and environment (on startup.)

We will delve much deeper in to Django settings in a future post, but for now let’s assume we have both settings.VERSION and settings.ENV configured with the appropriate values for the Django apps to make use of.

Creating custom signals is a one-liner and we’ll create a signals.py file in which it’ll live, the contents of which will look something like this.

#
#
#

from __future__ import absolute_import

from django.dispatch import Signal

# create our startup signal and specify the two arguments we'll be passing
# to it
startup_signal = Signal(providing_args=('version', 'env'))

That’s all there is to defining the signal, now the trick in this case is picking a good place to do the send. One of the best options is at the end of your last app’s models.py file. It will be one of the last things imported and by that point the bulk of Django’s setup will be complete (including settings and logging, which is important for our use-case.)

We’ll add the following to our models.py file.

#
#
#

from __future__ import absolute_import

from django.conf import settings
from startup.foo.signals import startup_signal

...

# we've started up, models have been loaded, send the signal
startup_signal.send(sender=startup_signal,
                    version=settings.VERSION, env=settings.ENV)

We’re just about done, the only thing remaining is to connect our signal handler(s.) In this trivial case this code can live just above the signal send, but the beauty of using the signal sub-system is that it can go where ever you need it so long as it is imported before the final models.py file. You can also have as many callbacks connected as you need. The callback we’ll use to log our version and environment information is as follows.

#
#
#

from __future__ import absolute_import

from startup.foo.signals import startup_signal
import logging

logger = logging.getLogger(__name__)

...

def on_startup(sender, version, env, **kwargs):
    logger.info('startup: running version=%s, with env=%s',
                version, env)

# example
startup_signal.connect(on_startup, dispatch_uid='models-startup')

And that’s it, a quick example of defining a custom signal in Django that is sent out during startup and with an added bonus of getting the version and environment in to the logs. As always, feel free to comment or email with any questions or suggestions.