Request for Comments: Auto-Installation of Apps in Django

by Corey Oordt •  Published 15 Sep 2009

The problem of pluggable Django apps

Lately we’ve come across a bit of an issue with dependencies. We use pip and virtualenv for our projects and it works great for installing Python dependencies.

However it is not so great if the dependency is a Django pluggable application. There is more you have to do: add all the dependent apps in INSTALLED_APPS and add the appropriate entry in your base urls.py. This can be a problem if you aren’t familiar with the app, let alone its dependent apps.

Recently I used pip to test out what I thought was a stand-alone Django pluggable app, only to discover dependency after dependency. This isn’t very pluggable in my book.

Proposal: A method of auto-installing apps

The Django community has developed several patterns that make development easier. Many of them are related to how you configure the settings.py file for your project. See examples from Usware and this presentation from EuroDjangoCon. I’m proposing a new one, and would like assistance in refining it.

  • A pluggable application would contain its own settings.py file containing one or more standard settings: APP_NAME, VERSION, and BASE_URL_CONF.
  • In the project’s settings.py file, you would loop through one or more paths containing Django apps/Python packages.
  • For each Python package, if it is not already in INSTALLED_APPS, attempt to import the settings module and see if it has an APP_NAME attribute.
  • If it does, add it to INSTALLED_APPS and then check for a BASE_URL_CONF attribute.
  • Add that to a list so you can append/prepend it to your urls.py.

This system IMHO is:

  • Simple to use
  • Easily turned off by adding the app to INSTALLED_APPS
  • Doesn’t interfere with any other packages or functionality
  • Easily extensible or customizable

Example code is in a gist at git, but I’ll show it here:

import importlib, os
APP_DIRS = (os.path.abspath(os.path.join(PROJECT_ROOT, 'test')),)
sys.path.extend(APP_DIRS)
BASE_URL_CONFS = []
for app_dir in APP_DIRS:
    for app in os.listdir(app_dir):
        if not app.startswith('.') and app not in INSTALLED_APPS:
            try:
                app_settings = importlib.import_module('%s.settings' % app)
                if getattr(app_settings, 'APP_NAME', '') != '':
                    print "Auto Installed %s" % app
                    INSTALLED_APPS = INSTALLED_APPS + (app,)
                base_url_conf = getattr(app_settings, 'BASE_URL_CONF', '')
                if base_url_conf != '':
                    BASE_URL_CONFS.append(base_url_conf)
            except ImportError:
                pass

I’m sure there can be some tweaking to the code, but it gets the idea across. Please feel free to send comments to me at coordt _at_ washingtontimes.com, or leave a comment down below.

blog comments powered by Disqus