Kristian Glass - Do I Smell Burning?

Mostly technical things

Django and Static Files

Django’s handling of static files is great, but sometimes causes confusion. If you’re wondering how it all fits together, what some of the settings mean, or just want some example uses, then keep reading.

Introduction

A typical Django project will have multiple sets of static files. The two common sources are applications with a static directory for media specific to them, and a similarly-named directory somewhere in the project for media tying the whole project together.

Ultimately, you want these all to end up in one place, to be served to the end user. This is where the collectstatic command comes in; as the name suggests, it’ll collect all your static files together into that one place. Of course, if you have DEBUG set to True in your settings, runserver will happily handle all this for you, but that won’t be the case for your final deployment (and nor should it be!)

So how does this all happen?

Configuration

Finders

First of all, where to find the static files?

By default settings.py will be created with a STATICFILES_FINDERS setting, with a value of:

settings.py
1
2
('django.contrib.staticfiles.finders.FileSystemFinder',
 'django.contrib.staticfiles.finders.AppDirectoriesFinder')

These will be used to find the source static files. The AppDirectoriesFinder is responsible for picking up $app_name/static/, the FileSystemFinder uses the directories specified in the STATICFILES_DIRS tuple.

You’ll probably want STATICFILES_DIRS to look something like the below:

settings.py
1
2
3
4
5
# Because actually hard-coding absolute paths into your code would be bad...
import os
PROJECT_DIR = os.path.dirname(__file__)

STATICFILES_DIRS = (os.path.join(PROJECT_DIR, 'static'),)

Storage

The STATICFILES_STORAGE setting controls how the files are aggregated together. The default value is django.contrib.staticfiles.storage.StaticFilesStorage which will copy the collected files to the directory specified by STATIC_ROOT.

Do not confuse STATIC_ROOT, to where static files are collected, with the aforementioned STATICFILES_DIRS; the former is output, the latter are inputs. They should not overlap. This is a common mistake.

Update: To be absolutely clear, STATIC_ROOT should live outside of your Django project – it’s the directory to where your static files are collected, for use by a local webserver or similar; Django’s involvement with that directory should end once your static files have been collected there

URL

Last but not least, STATIC_URL should be the URL at which a user / client / browser can reach the static files that have been aggregated by collectstatic.

If you’re using the default StaticFilesStorage, then this will be the location of where your nginx (or similar) instance is serving up STATIC_ROOT, e.g. the default /static/, or, better, something like http://static.example.com/. If you’re using Amazon S3 this will be http://your_s3_bucket.s3.amazonaws.com/. Essentially, this is wholly dependent on whatever technique you’re using to host your static files. It’s a URL, and not a file path

Common Mistakes

  • Overlap between STATICFILES_DIRS and STATIC_ROOT – the former is a set of places to look for static files, the latter is where they’re stored
  • Incorrect STATIC_URL – it’s a URL, not a file path
  • Incorrect configuration of whatever you’re using to host your static content; this is why I use S3, in my experience it’s the least effort to get working
  • Having STATIC_ROOT inside your project directory. While not strictly a mistake, it’s not where it belongs, and is generally a sign of other misunderstandings

Examples

All of these will assume you’ve left STATICFILES_FINDERS as its default, and STATICFILES_DIRS as described above; I’ve never yet had a reason, across dozens of projects, for these not to be the case

Apache serving static files

settings.py
1
2
STATIC_ROOT = '/srv/www/com.example.static/'
STATIC_URL = 'http://static.example.com/`

Apache config (e.g. /etc/apache2/sites-enabled/com.example.static.conf):

com.example.static.conf
1
2
3
4
<Virtualhost *:80>
    DocumentRoot /srv/www/com.example.static
    ServerName static.example.com
</Virtualhost>

Amazon S3

(Update: If you want more information on this topic, check out my follow-up blog post, Using Amazon S3 to host your Django Static Files)

Note this uses django-storages, which is a nice wide-ranging collection of custom backends for STATICFILES_STORAGE described above.

settings.py
1
2
3
4
INSTALLED_APPS += ('storages',)
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_STORAGE_BUCKET_NAME = 'my_bucket_name'
STATIC_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME

I frequently deploy to Heroku where custom httpd configuration is nontrivial, so often use this method.

Conclusion

Static file handling is important to get right, and straight forward once you know how, but easy to get wrong. I hope this clarifies things.

As ever, drop me a line if you have any queries, questions or complaints.

Did this help? Check out my book

Ok so I’m still writing the book so you can’t buy it just yet. But if you want to make sure that you’re serving your static files in the best way possible, you’ll want it:

Check out the book