Home

Feb. 1, 2022, 2 min read

Django Cache per User

Django's builtin cache_page() will only cache based on the URL, but by default not on the fact which user is currently authenticated (same for its {% cache ... %} template tag).

This is a massive footgun in my opinion, as it is very easy to write user-specific data to a shared cache that can then be seen by all other users.

For the template tag, this needs to be manually thought of by keying the cache to some user-specific template context attribute, like:

{% cache 'my-cache' request.user %} 
  ... 
{% endcache %}

For caching a whole page/view however, we need to combine cache_page() with vary_on_cookie() (as long as you are using some sort of cookie for authentication, so that we can assume each user has a unique cookie that is used for the cache key). This would be used like:

from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie

@cache_per_user(60)
@vary_on_cookie
@login_required
def my_view(request):
    ...

To not accidentally forget one of the decorators or mixup their order, this decorator can be used for convenience:

import functools

from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie

def cache_page_per_user(seconds: int) -> callable:

    def view_decorator(page_view : callable):
        @cache_page(seconds)
        @vary_on_cookie
        @functools.wraps(page_view)
        def cacheable_view(*args, **kwargs):
            return page_view(*args, **kwargs)

        return cacheable_view

    return view_decorator