django static files versioning

Pydev UA picture Pydev UA · Feb 3, 2012 · Viewed 19.3k times · Source

I'm working on some universal solution for problem with static files and updates in it.

Example: let's say there was site with /static/styles.css file - and site was used for a long time - so a lot of visitors cached this file in browser

Now we doing changes in this css file, and update on server, but some users still have old version (despite modification date returned by server)

The obvious solution is to add some version to file /static/styles.css?v=1.1 but in this case developer must track changes in this file and manually increase version

A second solution is to count the md5 hash of the file and add it to the url /static/styels.css/?v={mdp5hashvalue} which looks much better, but md5 should be calculated automatically somehow.

they possible way I see it - create some template tag like this

{% static_file  "style.css" %}

which will render

<link src="/static/style.css?v=md5hash">

BUT, I do not want this tag to calculate md5 on every page load, and I do not want to store hash in django-cache, because then we will have to clear after updating file...

any thoughts ?

Answer

MiniQuark picture MiniQuark · Feb 15, 2013

Django 1.4 now includes CachedStaticFilesStorage which does exactly what you need (well... almost).

Since Django 2.2 ManifestStaticFilesStorage should be used instead of CachedStaticFilesStorage.

You use it with the manage.py collectstatic task. All static files are collected from your applications, as usual, but this storage manager also creates a copy of each file with the MD5 hash appended to the name. So for example, say you have a css/styles.css file, it will also create something like css/styles.55e7cbb9ba48.css.

Of course, as you mentioned, the problem is that you don't want your views and templates calculating the MD5 hash all the time to find out the appropriate URLs to generate. The solution is caching. Ok, you asked for a solution without caching, I'm sorry, that's why I said almost. But there's no reason to reject caching, really. CachedStaticFilesStorage uses a specific cache named staticfiles. By default, it will use your existing cache system, and voilà! But if you don't want it to use your regular cache, perhaps because it's a distributed memcache and you want to avoid the overhead of network queries just to get static file names, then you can setup a specific RAM cache just for staticfiles. It's easier than it sounds: check out this excellent blog post. Here's what it would look like:

CACHES = {
  'default': {
    'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
    'LOCATION': '127.0.0.1:11211',
  },
  'staticfiles': {
    'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    'LOCATION': 'staticfiles-filehashes'
  }
}