I don't really blog anymore. Click here to go to my main website.

muhuk's blog

Nature, to Be Commanded, Must Be Obeyed

March 12, 2009

Django: Where Should My App Live

Python is my preferred language and Django is my preferred environment for web applications. Django’s probably the most important strength is how it splits different parts of an application and allows (and encourages) decoupling between them. This way you get to have many small re-usable and, if designed well, highly configurable apps (in Django terms).

The frameworks itself comes with a set of apps, contrib apps. These are officially supported applications for most general tasks. For example Django has a syndication app that allows you to create feeds for your content easily, a comments app for.. well comments, a redirects app for dynamically managing URL redirects. And let’s not forget the renowned admin app.

Third party re-usable apps can be used to complement contrib apps. These apps provide more specific funtionality, such as providing a dynamically generated robots.txt. Third party apps usually have no dependencies other than Django (and therefore contrib apps). Best third party apps are the ones that provide just enough functionality on one task. It’s the similarity with UNIX philosophy that makes Django apps so attractive; do one thing, do it well.

Finally there is your project specific code. Although it is not a must, I think it is a good design pattern to put project specific code in one or more project specific apps. These apps will probably have lots of dependencies to other apps, but that is cool. These apps will only function as glue between re-usable apps.

Django Default

How does django-admin (or manage.py within your project) handle apps? “manage.py startapp appname” command creates a new app inside your project directory with the following file structure:

appname/
    __init__.py
    models.py
    views.py

And then, assuming your project directory name is myproject, you can import your models like “from myproject.appname.models import Foo”. Two things to note here:

  • Your new app is inside your project directory. This is normal, but significant at the same time. More on this later.
  • Your code is coupled with the project directory’s name. You might be fine with it in the beginning, but at some point you should realize that the project you are working on is not a library, it is an application. Therefore it ought to be independent of the containing folder name. But the real problem is that your app is coupled with the project, tightly.

The first point is not an obstacle until you decide to distribute your app individually. An app is just a library, it can live anywhere inside your PYTHONPATH. In other words; as long as you can import it, location of your app is irrelevant.

The second point is important. If you plan to re-use your apps, you need to:

  1. Make sure you have cleaned up all project specific code.
  2. Minimize dependencies to other apps, especially non-contrib apps.

Adding Project Directory To PYTHONPATH

A quick fix could be to add the project directory to PYTHONPATH. This makes all libraries within accessible from python.

You don’t need to modify PYTHONPATH for development server. But for both development and production environments you will need to modify your settings.py with the following:

import os

# Absolute path for the project directory
_PATH = os.path.abspath(os.path.dirname(__file__))

# Name of the project directory
_MODULE = os.path.basename(_PATH)


# Define absolute path for media
MEDIA_ROOT = _PATH + '/media/'

# Decouple project directory
ROOT_URLCONF = _MODULE + '.urls'

# Import apps within porject directory
# independent from its name
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    _MODULE + '.myapp',
    _MODULE + '.yourapp',
)

This only makes project directory an independent variable, adding a bit of flexibility. But your apps are still within the project directory and from a practical point of view; they are not yet ready to be shared between projects.

Seperating Apps From Projects

As I said above, and app is a library. If you can import it, it can live anywhere. I personally prefer putting all my re-usable apps in one directory and symlinking them somewhere within PYTHONPATH

# Import apps directly
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'myapp',
    'yourapp',
)

Now if your app is decoupled in code level it should be re-usable since we have decoupled it from the project directory. This, however easy at the beginning, introduces a little more work in terms of maintenance. You need to deploy and maintain your re-usable apps and projects seperately.

Lifetime Of An App

I always do my best to model any app as re-usable. But to speed up my first few iterations I follow the plan below:

  1. First thing I do when I create a new project is to set up _MODULE & _PATH in my settings.py.
  2. Initially I allow my apps to be tightly coupled. Adding customizability and extensibility where it is easy.
  3. Once it functions the way I want, I start de-coupling. If it is truly project-specific code, I just spend minimal effort de-coupling. I don’t think this is a waste of time. It will probably pay off in the future as less maintenance cost.
  4. When the app has minimal dependencies, I move it to my django apps dir. So far I have had the advantage of using the same (source control) repository with the project. But now the app is mature and it should live in its own repository.

If you have any questions, suggestions or corrections feel free to drop me a line.