Entry point hook for Django projects

The age old question of "How do I run code when Django starts?" may now be finally answered.

Django 1.4 made a big improvement with how Django is started. The manage.py script has been simplified and made more explicit. This allows for better customization of the bootstrap process of Django and your project.

A common question in the Django community is "how do I run some code at start up?" The best way to implement this is to simply run some code at the entry points of Django (after we ensure DJANGO_SETTINGS_MODULE is set.) These entry points are: manage.py and wsgi.py.

The manage.py script is the most common entry point in development, but can also be used to start production role code. The wsgi.py script is used to interface with WSGI servers.

In Pinax we've set out to solve this issue and provide a common place that you can guarantee will be run during the start up of Django. One of the most common tasks is to register signals. A workaround is to register them through urls.py, but this isn't ideal and can cause problems.

The solution is really simple. Using project template code here is how entry points may look.

manage.py:

import os
import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")
    
    import {{ project_name }}.startup as startup
    startup.run()
    
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)

wsgi.py:

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings")

import {{ project_name }}.startup as startup
startup.run()

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

This will import a startup.py file located in your project's package. The only requirement is that you define the run method. In Pinax we decided to take it a little bit further. Since registering signals is a common task to do we wrote a utility function we put in our startup.py:

from django.conf import settings
from django.utils.importlib import import_module
from django.utils.module_loading import module_has_submodule


def autoload(submodules):
    for app in settings.INSTALLED_APPS:
        mod = import_module(app)
        for submodule in submodules:
            try:
                import_module("{}.{}".format(app, submodule))
            except:
                if module_has_submodule(mod, submodule):
                    raise


def run():
    autoload(["receivers"])

Now, when Django starts up all app's receivers.py are imported ensuring they get registered at the appropriate time. No more urls.py or models.py hacks.

This approach now ships with all Pinax projects. You can check out the starter projects on the Pinax Github account. Look for repositories beginning with pinax-project-.

comments powered by Disqus