Django and Relativity

My default Django settings file has changed over time to now include settings that do not depend on the location of the project on the filesystem. This is great in a team environment where more than one person is working on the same project, or when deploying your project to a web server that likely has different paths to the project root directory.

There are essentially 2 places that are looking for a filesystem path:

  1. Templates
  2. Media

Let's assume the following directory structure for a Django project:

Example directory structure

Example Django project directory structure

Project Root

One of the first things in setting up relative filesystem paths for your template and media settings is to determine the root of the project. This can be accomplished easily enough by putting the following lines in your settings.py file:

# settings.py
import os
PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))

This gets the absolute path using the settings.py file as its source. For example, if the path to your settings file was /www/myproject/settings.py, PROJECT_PATH would be set to /www/myproject. This is exactly what we want as a base for specifying our template and media folders (assuming the above folder structure is what you use).

Templates

Now that we know the root path to our project, let's use that setting to tell Django where our templates are located...

# settings.py
TEMPLATE_DIRS = (
    os.path.join(PROJECT_PATH, 'templates')
)

Continuing our example, this would assume that our template directory is located just inside the root directory of our Django project. It is using os.path's join command to join the path names using the underlying operating system's default directory separator.

Media

In our example, the media folder (which would contain things like images, style sheets, and/or javascript) is a sibling folder of our Django project folder. The way we can specify this relatively from our Django project folder would be, for example, /www/myproject/../media. To accomplish this same thing in our urls.py file would look like the following example:

# urls.py
import os
from django.conf import settings

if settings.DEBUG:
    urlpatterns += patterns('',
        (r'^media/(.*)$', 'django.views.static.serve', {'document_root': os.path.join(settings.PROJECT_PATH, '..', 'media')}),
    )

If settings.MEDIA_URL is set to '/media/', this will match the above pattern and use the built-in generic view to serve the static content. Note that this example also uses a common pattern of only serving media using Django's built-in web server when DEBUG is set to true.

While not shown here, the same can be done for Django's admin media if it happens to be hosted relative to your project. That isn't true in my case -- I have Django's admin media in a shared directory that all of my Django projects can share.

mod_wsgi

Again, assuming the directory structure above, a sample mod_wsgi file for Django would look like the following:

# django.wsgi
import os, sys
import django.core.handlers.wsgi

sys.path.append(os.path.join(os.path.realpath(os.path.dirname(__file__)), '..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'django_project.settings'
application = django.core.handlers.wsgi.WSGIHandler()

Here we're getting the absolute path to the django.wsgi file and hopping up one directory from that to set our Python path for mod_wsgi to use. It is in this directory that the root of the Django project is located.

Conclusion

With the above changes, your settings no longer depend on any file system paths that only reside on one particular machine and the project is free to move about without worry of needing to update the settings to specify the correct path -- everything is relative.

Updates

  • 2008-08-12: Updated code snippets to import the os module in its entirety. It's already imported in Django, so importing it again (or importing any submodule) just grabs the reference to the module already in memory.

  • Updated 2008-09-09: Updated to use dirname rather that splitting the file. Who can argue with cleaner more explicit code.

  • Updated 2008-10-01: Updated to include an example of a mod_wsgi file with relative path.

  • Updated 2009-04-05: Updated to use realpath instead of abspath as it is the most canonical. See the updated blog post Django and Relativity Updated.

About this entry

Date Posted:
June 20th 2008 at 3:06:01 PM

Tagged:
django

Previous Entry:
Command Line History

Next Entry:
Using Gmail's SMTP server from Django