Jan 2012

#django
#static

Setting up the new 'staticfiles' app on Django 1.3


Even if I've been using Django 1.3 for a while now, I've been holding off on some of it new features, such as the new way to handle static files via a new app called (guess what) staticfiles . Essentially, what the new static app does is allowing you to leave all static files within the /static directory of each of the django apps you're using. This is for development; when you're deploying and (most likely) want these files to be served via a (faster) separate server process, the staticfiles app helps you gather all of them into a predefined directory by using a handy shell command.

That's the gist of it. I finally took a look at django.contrib.staticfiles last week, so here's a brief report on how to get it to work (p.s. a couple of related threads on stack overflow can be found here and here).

1. Set up the static and media paths settings

My previous settings looked like this:

# the site root is one level up from where settings.py is
SITE_ROOT = os.path.dirname(os.path.realpath(__file__)).rsplit('/', 1)\[0]
MEDIA_ROOT = os.path.join(SITE_ROOT, 'static')
MEDIA_URL = '/static/'
ADMIN_MEDIA_PREFIX = '/static/adminmedia1.3/'

The media root contained all of my static files, some of which I had to copy in there manually, each time I added a new django app to my project. On the production server, the MEDIA_URL is mapped to a local path (in the apache conf settings) that is essentially a duplicate of the /static directory we have in the development server. The only difference, the static stuff is delivered directly by Apache, bypassing django (=so to make it faster).

The new way of declaring these variables is this instead:

MEDIA_URL = '/media/uploads/'
STATIC_URL = '/media/static/'
ADMIN_MEDIA_PREFIX = '/media/static/admin/'

# Absolute path to the directory that holds media uploaded
# I keep the uploads folder at the project-root level server
MEDIA_ROOT = os.path.join(SITE_ROOT, 'uploads') 

# physical location of extra static files in development server
STATICFILES_DIRS = (
    os.path.join(SITE_ROOT, 'myproject/static'),
)
# path used with "python manage.py collectstatic"
# I normally put this at the project-root level that contains also the wsgi files for apache
STATIC_ROOT = os.path.join(SITE_ROOT, 'apache/static')

Obviously on a production server, you will have to set up the required aliases in the apache conf file, so that MEDIA_URL and STATIC_URL are pointing at the right physical locations:

Alias /media/uploads/ /path/to/mysite.com/uploads/
Alias /media/static/ /path/to/mysite.com/apache/static/

The django docs explain the new approach with these words:

In previous versions of Django, it was common to place static assets in MEDIA_ROOT along with user-uploaded files, and serve them both at MEDIA_URL. Part of the purpose of introducing the staticfiles app is to make it easier to keep static files separate from user-uploaded files.

For this reason, you need to make your MEDIA_ROOT and MEDIA_URL different from your STATIC_ROOT and STATIC_URL.

The STATIC_ROOT directory is not necessary in the development server: in fact is only used if you call the collectstatic manangement command. It's not needed to add the directory to the STATICFILES_DIRS setting to serve your static files!

Furthemore, the STATICFILES_DIRS variable tells Django of the location of static files which are not within specific applications. Mind that during the 'collection' operations also these files will be copied into the STATIC_ROOT directory.

2. Add context processors and the app

Add the required context processor in setting.py:

TEMPLATE_CONTEXT_PROCESSORS += 'django.core.context_processors.static'

# Add also the new static app to your installed apps:

INSTALLED_APPS = (
....    
    'django.contrib.staticfiles',    
.....
)

Keep in mind that it's very likely that in your templates you will have to manually change all references to MEDIA_URL into STATIC_URL!

3. Url configuration

On your development server, this is what you would do:

from django.contrib.staticfiles.urls import staticfiles_urlpatterns
# ... the rest of your URLconf goes here ...
urlpatterns += staticfiles_urlpatterns()

This will inspect your STATIC_URL setting and wire up the view to serve static files accordingly. Don't forget to set the STATICFILES_DIRS setting appropriately to let django.contrib.staticfiles know where to look for files additionally to files in app directories.

WARNING: the staticfiles_urlpatterns helper function will only work if DEBUG is True and your STATIC_URL setting is neither empty nor a full URL such as http://static.example.com/ (more info here).

Finally, mind that in this new approach you will need to arrange for serving of files in MEDIA_ROOT yourself; staticfiles does not deal with user-uploaded files at all. You can, however, use django.views.static.serve() view for serving MEDIA_ROOT in development (for more info see Serving other directories).

if settings.LOCAL_SERVER:     # ===> static files on local machine    
    urlpatterns += patterns('', 
        (r'^media/uploads/(?P<path>.\*)$', 'django.views.static.serve', 
            {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
        )

In the end, I conflated the two things into this code (ps I've added a variable called LOCAL_SERVER to quickly see which platform I'm on):

if settings.LOCAL_SERVER:     # ===> static files on local machine
    from django.contrib.staticfiles.urls import staticfiles_urlpatterns
    urlpatterns += staticfiles_urlpatterns()    
    urlpatterns += patterns('', 
        (r'^media/uploads/(?P<path>.\*)$', 'django.views.static.serve', 
            {'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
        )

4. On your production server

Easy: in your urlconf there's no need to do anything, as static urls are usually handled by Apache directly. However for that to happen what you have to do is collect all static files into the directory that apache is looking into, that is, the one specified with the STATIC_ROOT setting. This is how you do it:

$ `python manage.py collectstatic`

This shell command will a) look in the /static/ directory of each of the apps of yours INSTALLED_APPS setting. b) look in directories you specify in the STATICFILES_DIRS setting.

…and copy whatever it finds into the STATIC_ROOT directory, as defined in your settings (ps: it'll preserve the original directory structure).

That's all folks!

Cite this blog post:


Michele Pasin. Setting up the new 'staticfiles' app on Django 1.3. Blog post on www.michelepasin.org. Published on Jan. 24, 2012.

Comments:


See also: