Since Django's cache_page is rubbish with middleware, I had to write a new simple middleware that will allow you to opt-in pages to caching (like @cache_page
), however will work with various middlewares (e.g. sessions, languages), like the usually Django per-site cache.
Code
This is the code, just put it in a file somewhere
from functools import wraps
class DefaultDontCacheMiddleware():
"""
A middleware that will mark every request as not to be cached.
This allows one to use the Django (Update|FetchFrom)CacheMiddleware
middlewares without having caching turned on for every view. This allows an
opt-in cache, not opt-out
"""
def process_request(self, request):
request._cache_update_cache = False
def mark_for_caching(func):
"""
Decorator that enabled caching for this page.
It's similar to ``cache_page`` decorator, but will work with middlewares
that set headers (e.g. LocaleMiddleWare, SessionMiddleware,
AuthenticationMiddleware etc.)
"""
@wraps(func)
def newfunc(request, *args, **kwargs):
request._cache_update_cache = True
return func(request, *args, **kwargs)
return newfunc
Usage
Middleware
You have to load the DefaultDontCacheMiddleware
after the django.middleware.cache.FetchFromCacheMiddleware
, this is because the FetchFromCacheMiddleware will, if there is a cache miss, mark that request as something to cache in the UpdateCacheMiddleware. We must override it's behaviour, and tell it not to cache anything by default
View decorator
For each view that you want cached, just put the @mark_for_caching
decorator on it, in the same way that you use the @cache_page
decorator.
Explaination
I had to look through the Django caching code to try to figure out what was going on, and saw that the cache will only be updated if the request
has the _cache_update_cache
attritube True. This allows you to prevent caching. It is set to True by the FetchFromCacheMiddleware
if there is a cache miss, so that the results of the view function will be cached. After the FetchFromCacheMiddleware
is called, our DefaultDontCacheMiddleware
is called and it sets that to False, so that by the time it gets back up to the UpdateCacheMiddleware
, it's not cached.
However if the view goes through the @mark_for_caching
decorator, then that value is set to it's value of True, and the UpdateCacheMiddleware
will store the result, along with any of the Vary
headers or Cookie
/Content-Language
headers as apporpriate.