Cache a django view that has URL parameters

It appears that you no longer need to do anything more complicated than placing @cache_page([length of time]) above your View function you are trying to cache, irrespective of whether you have parameters in the URL.

For example, if you have a url like:

http://example.com/user/some_user_id

Your view function in views.py would look something like this:

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

@cache_page(60 * 10)
def get_user_detail(request, user_id=None):
    ...
    return render(...)

Right, vary headers is not the correct solution, it's used when you want to cache based on client request headers like user-agent etc.

You'll need to use low-level API or template fragment caching. It depends on your views really.

With low-level API it looks something like this:

from django.core.cache import cache

def get_user(request):
    user_id = request.GET.get("user_id")
    user = cache.get("user_id_%s"%user_id)
    if user is None:
        user = User.objects.get(pk=user_id)
        cache.set("user_id_%s"%user_id, user, 10*60) # 10 minutes
    ...
    ..
    .

Yes, you can use django-view-cache-utils, here is code for your case:

from view_cache_utils import cache_page_with_prefix
from django.utils.hashcompat import md5_constructor
...
@cache_page_with_prefix(60*15, lambda request: md5_constructor(request.get_full_path()).hexdigest())
def my_view(request):
    ...

From my reading of the source code and empirical testing, the @cache_page decorator natively handles GET parameters correctly (at least in Django 2.2).

Digging through the source:

  1. The decorator is defined in django.views.decorators.cache
  2. It calls django.utils.decorators.decorator_from_middleware_with_args()
  3. Which calls django.utils.decorators.make_middleware_decorator()
  4. Which is a silly level of complex. A veritable onion of functions returning functions. The important bit is it calls middleware.process_request() where 'middleware' is django.middleware.cache.CacheMiddleware.
  5. Which calls django.utils.cache.get_cache_key() to generate the cache key.
  6. Which calls django.utils.cache._generate_cache_header_key().
  7. Which calls request.build_absolute_uri() where 'request' is django.http.request.HttpRequest
  8. Which calls django.http.request.HttpRequest.get_full_path()
  9. Which calls django.http.request.HttpRequest._get_full_path()
  10. Which, finally, includes self.META.get('QUERY_STRING', '') in the string it returns.

On the flip side, when the response is complete a similar path goes through middleware.process_response() which eventually calls django.utils.cache._generate_cache_header_key() to determine where to store the response in the cache.

Empirically, I can see that requests to the decorated view are being cached and the response changes when the GET parameters change.