Django caching middleware and authenticated users

Orde Saunders' avatarUpdated: Published: by Orde Saunders

In the process of moving this blog from Toto to Django I had to modify the behavior of the way the built in caching middleware deals with authenticated users.

Toto is a very good simple Git based blog engine which enabled me to get a blog up and running with minimal effort. The trade off for this is a lack of control over the lower level details and a reliance on Git and SSH for adding or updating posts which rules out my phone and tablet. I have now re-implemented the site using Django following the format set out by Toto - had I decided to start by implementing the blog from scratch in Django I would probably still be procrastinating about URL structures and data formats constraints can help get things done.

As this is a fairly simple site I have made use of the Django caching middleware which provides a way to both cache views and set HTTP cache headers with two lines of code - django.middleware.cache.UpdateCacheMiddleware caches generated responses and django.middleware.cache.FetchFromCacheMiddleware fetchs previously cached responses to save generating the response again. There is a useful setting that prevents caching of requests for authenticated users but my reading of the documentation didn't match the behaviour I was seeing. The documentation states:

Optionally, if the CACHE_MIDDLEWARE_ANONYMOUS_ONLY setting is True, only anonymous requests (i.e., not those made by a logged-in user) will be cached. This is a simple and effective way of disabling caching for any user-specific pages (including Django's admin interface).

Once of the biggest advantages of open source is the ability to inspect the code to understand the behaviour of a component and when this setting is True it will not place the output of any request generated by an authenticated user into the cache and this is obviously desirable behaviour. What caught me out was that FetchFromCacheMiddleware does not take account of the CACHE_MIDDLEWARE_ANONYMOUS_ONLY and, if a cached response generated by an anonymous user is found in the cache, it will serve that to an authenticated user.

Subclassing django.middleware.cache.FetchFromCacheMiddleware

The way Django uses Python classes and its high degree of modularity means that changing the behaviour of the built in middleware is as simple as subclassing it with the addition of a conditional as shown below and changing the MIDDLEWARE_CLASSES setting to use this new class.

from django.middleware.cache import FetchFromCacheMiddleware as DjangoFetchFromCacheMiddleware

class FetchFromCacheMiddleware(DjangoFetchFromCacheMiddleware):

    def process_request(self, request):
        if self.cache_anonymous_only and request.user.is_authenticated():
            return None
            return super(FetchFromCacheMiddleware, self).process_request(request)

By extending existing functionality in this way there have been no changes made to the distributed Django libraries and this implementation will benefit from all the existing behavior andany bug fixes or enhancements made to this component in the future, does not require any patches to be re-applied to the distributed version of Django when updating and, because it does not alter the original code it does not break anything that relies on the original functionality and still allows the original functionality to be used if required.