Fragments of Code

January 7, 2009

Managing static files for Django applications

Filed under: Apache, Django — Tags: , , — David Chandek-Stark @ 3:30 pm

Two principles of Django development lead to a dilemma:

  1. Application code should be self-contained — i.e., not coupled with a project.
  2. Django should not serve static media files (for security and efficiency).

So, how does one manage static files (images, css, js, etc.) that are bundled with an application?  I make a couple of assumptions:

  1. You don’t want to hard-code full URL paths in templates, so you need some way to inject a base URL dynamically into your template context.
  2. You want to keep the media files in the application package — that is, not to copy or move them to a filesystem location outside the application directory.

Django’s builtin settings provide for two non-admin media settings, MEDIA_ROOT and MEDIA_URL.  One option for resolving the issue is to use MEDIA_URL and create symlinks from the MEDIA_ROOT directory to the application’s media directory (or directories).  Personally, I don’t like that, partly because I prefer not to use symlinks, but mostly because the MEDIA_ROOT space is used for uploads for model file fields, and it feels like this other static, presentation-related, content should be in its own space.  OTOH the symlink approach is probably the most flexible.

What I’ve been doing to this point is based on the assumption that my application packages all live in the same base directory. I added a custom setting APP_MEDIA_PREFIX (inspired by ADMIN_MEDIA_PREFIX) and set it to the URL path which I alias in in Apache.

Django setting:

APP_MEDIA_PREFIX = '/django/apps/'

Apache conf:

# Application media
AliasMatch ^/django/apps/([^/]+)/media/(.+) /opt/django/apps/$1/media/$2
<DirectoryMatch "^/opt/django/apps/[^/]+/media">
    Allow from all
</DirectoryMatch>

My apps packages are in /opt/django/apps and by convention put their media files in a “media” subdirectory. Then I created a custom template tag for printing APP_MEDIA_PREFIX (inspired by {% admin_media_prefix %}) in my custom template tag module (custom.py):

from django import template
from django.conf import settings

register = template.Library()

@register.simple_tag
def app_media_prefix():
    """Prints value of APP_MEDIA_PREFIX setting.

    Usage: {% app_media_prefix %}
    """
    return getattr(settings, 'APP_MEDIA_PREFIX', '')

Then, in a template, for example:

{% load custom %}
<link rel="stylesheet" type="text/css" href="{% app_media_prefix %}locationguide/media/css/locationguide.css"/>

In this case, the application name/label is “locationguide” and is located in /opt/django/apps/locationguide.

If anyone has thought of a significantly better way to manage this scenario, I’d love to hear it.

December 19, 2008

Re-organizing my Django repository

Filed under: Django — Tags: , , — David Chandek-Stark @ 10:34 am

Recently I did some significant reorganization of my Django applications and projects in order to more fully decouple them.  Django is clearly intended to operate this way, but as James Bennett acknowledges in a post to Django users, the Django tutorial puts apps inside the project for simplicity, and I had followed this pattern into production.  I took his advice in that same post to move my apps to top-level Python packages and out of the project directory. This was my initial setup under /opt/django:

apps/ (Project)
    __init__.py
    admin.py
    settings.py
    urls.py
    manage.py
    myapp1/
        __init__.py
        admin.py
        models.py
        views.py
        urls.py
    myapps2/
        ...

The project directory was a top-level Python package, so my Apache/mod_python configuration had this:

PythonPath "['/opt/django'] + sys.path"
SetEnv DJANGO_SETTINGS_MODULE apps.settings

and my application modules were accessed via the project path. This presented no functional problems at the time since I had only one project. However, I did have multiple installations of Django for which I wanted different settings, URLs, and admin UIs. I should have treated these as separate projects, but I got around that by re-using the same project in different ways for each installation.  But I really wanted to keep all the source under version control and in a single tree (I was keeping admin.py, settings.py and urls.py out of the repository so they could be different for each installation), and it just bothered me to have project-path-dependent code.

After reorganizing, my repository looks like this:

apps/
    myapp1/
        __init__.py
        admin.py
        models.py
        views.py
        urls.py
    myapps2/
        ...
projects/
    myproject1/
        __init__.py
        settings.py
        admin.py
        urls.py
        manage.py
    myproject2/
        ...

and my Apache/mod_python conf:

PythonPath "['/opt/django', '/opt/django/apps', '/opt/django/projects'] + sys.path"
SetEnv DJANGO_SETTINGS_MODULE myproject1.settings

(I kept /opt/django on the PythonPath because there are some common packages there.)

Blog at WordPress.com.