Subscribe:   RSS icon   twitter icon

Avoid serving cached media files with Django’s development server

Lee Phillips — Feb. 28, 2012

When your Django application is deployed to the public web, its static files are served directly by Apache or Nginx without any involvement by Django or your Python code. At least, that’s what should be happening. Static files are stylesheets, images, anything sitting on your server that is returned in response to a request and does not need to be constructed dynamically. Apache or Nginx can serve these files fast, and can handle caching issues, telling the client when it already has the current version and need not download a new one.

But when you are developing a Django application, you typically serve it using Django’s development server to a browser on your local machine, rather than bothering with a real webserver such as Apache. This is usually more convenient for several reasons; the main one may be that the development server loads new code as you change it and need not be restarted.

One problem frequently arises. When you change your stylesheets or other static resources, you will find that your browser seems to stubbornly hold on to the stale, cached versions. Django’s development server doesn’t seem to know that the files have changed. Worse, it seems to be caching the files itself at times, as even telling your browser to reload the page often fails to refresh its cache. So now you are restarting the development server as if it were Apache.

There are several ways to deal with this problem. You can, for example, include some kind of unique string as part of the filename each time you alter a static file, using a hash or a timestamp. This will force your browser to fetch a new version, and force the development server to serve it. But that requires a lot of extra, tedious coding. I think I have a simpler solution. It’s been working for me, but I welcome comments and suggestions about better approaches.

The web applications I’m working on are in directories under /home/lee/webapps, and their static media files are stored in directories under /home/lee/webapps/media. First, your settings file must define the location for your media files. Here is a minimal example:

DEBUG = True
TIME_ZONE = 'America/New_York'
LANGUAGE_CODE = 'en-us'
MEDIA_ROOT = '/home/lee/webapps/media/'
MIDDLEWARE_CLASSES = ("django.middleware.common.CommonMiddleware",)
ROOT_URLCONF = 'webapps.urls'
INSTALLED_APPS = ( 'webapps.a.b', 'webapps.c.d')

Your urls file should route requests for media files to a function that we’ll write just to serve them. I call this function “serve” and keep it in a module called “staticserve”:

[ usual imports ]
urlpatterns = patterns('',
    [ routing for your webapps here ]
    (r'(.*.png)', 'webapps.staticserve.serve'),
    (r'(.*.css)', 'webapps.staticserve.serve'),
    [ add additional media types here ]
)

And here is the staticserve module:

from django.views.static import serve as staticserve
import webapps.settings as settings
def serve(request, what):
   response = staticserve(request, what,
      document_root=settings.MEDIA_ROOT)
   response['Cache-Control'] = 'no-cache'
   return response

That’s all there is to it! I’ve simply used Django’s supplied static.serve function, which returns an HttpResponse, and added a no-cache header. Note that simply using no-cache with your dynamic views will not work, because the browser makes a separate request for each media file. The usual method is to call django.views.static.serve directly from your urls file; these additional requests for media files will go directly there and not to any of your views, and be answered with a separate set of response headers.


Share with Facebook Share with Twitter Share with Reddit Share with Slashdot
▶ Comment
lee-phillips.org is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to amazon.com.
Quotilizer ... loading ...

Tenuously related:

The best tool for CVs with publication lists.