A class for the static files app in Django that invalidates outdated browser cache.
You add the line
STATICFILES_STORAGE = 'path.to.hashpathstaticfilesstorage.HashPathStaticFilesStorage'
to your settings.py and everywhere you use the 'static' templatetag it will append a hash calculated form the contents of the file as a GET parameter at the end of the URL for the file. Example:
{% static "path/to/file.txt" %} -> /static/path/to/file.txt?4e1243
This guarantees that every time you update your static files, whether it is an image, a CSS file or anything else, all browsers fetch the new version of the file instead of using their cached versions.
Remember to change the import path to match your setup (replace the 'path.to' with your actual import path).
You can reduce the time it takes to return the URL by caching the hashes when they are first calculated. The cache framework that ships with Django is used to store the hashes, so make sure you configure that before caching your hashes.
When you start using the cache, and every time after that when you have to invalidate the cache, you simply create an object of the type HashPathStaticFilesStorage and call the method 'invalidate_cache' on that object:
h = HashPathStaticFilesStorage()
h.invalidate_cache()
This should happen every time you alter or update a file in one of your /static folders.
This class introduces two new variables that you can put in your settings.py to configure it's behaviour:
- STATICFILES_HASH_ACCURACY An integer that controls the number of characters used in the hash. This should not be set below 3, as that increases the possibility of a collision. The hash can be at most 40 characters long so values above 40 have the same effect as 40. Defaults to 6
- STATICFILES_HASH_KEY_PREFIX A prefix for they keys in the cache. Defaults to 'staticfiles_hash'.
Django 1.4 or newer is required.
That definitely makes sense, thanks for clarifying, I didn't mean to imply that your solution is bad. I chose to have the hash in the filename in staticfiles storage backend because many CDNs and caching proxies (e.g. Amazon CloudFront) ignore the querystring and only look at the cache headers. Having separate files makes that easy enough to handle and allows serving old page caches gracefully.
That said, in case you want, you could pre-populate the cache when running collectstatic by hooking up the
post_process
method in your storage, see https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#django.contrib.staticfiles.storage.StaticFilesStorage.post_process That's the generic hook that theCachedStaticFileStorage
also uses but can be used for different things.