DRF: More granular caching with ListAPIView?


I have a few ListAPIViews (one example code below) that I would like to cache to improve performance (my queries are bit more complex and take hundreds of ms).

This implementation works, but it caches based on the URL, which is not optimal for me. The URL has parameter locale which can be basically any country code. But I am mapping this locale to 8 region ids and for those the results are the same.

I would also want to customize the response based on one header - similar situation to the locale → region, so vary_on_headers decorator won’t help me there.

Ideally I would like to keep the below implementation, but with the ability to transform the incoming request into cache key and return cached data if exists.

class ReleasedGamesListView(generics.ListAPIView):
    permission_classes = [permissions.AllowAny]
    serializer_class = GameSerializer
    pagination_class = GamesPagination

    @method_decorator(cache_page(45 * 60))
    def get(self, *args, **kwargs):
        return super().get(*args, **kwargs)

    def get_queryset(self):
        # omitted

Is this even a good idea? Wouldn’t it be better to move to APIView to get more control?

As for the actual caching I plan to use Redis, as I did for other endpoint - DRF: View response 300-600ms, time to optimize? - #14 by nemecek_f

I started browsing my way through the CacheMiddleware to see just how deep that rabbit hole goes - and to me, it appears to go pretty deep. (cache_page is defined as the output from decorator_from_middleware_with_args(CacheMiddleware), which itself comes from make_middleware_decorator, which takes a middleware class and returns a view decorator)

What follows is all conjecture - I’ve never done anything like this -

If I were looking at this type of situation, I’d probably create a custom cache more directly oriented toward handling this specific case - something that is more tuned to caching based upon the url patterns that you wish to manage.

If you want to keep the specific semantics (using method_decorator) and the name cache_page for the decorator, you could probably create your own middleware class for your own cache_page function, and import it in your view instead of the one from django.views.decorators.cache.

(Actually, now that I think about it, i did once create a custom middleware to examine url patterns to collect certain metrics. While the use-case wasn’t identical, the basic idea was the same - match url patterns based upon different parts of the url and record data accordingly.)

Hmm interesting!

I have very little experience with middlewares so this is quite surprising approach to me. Is the main reason that the existing ListAPIView isn’t really meant for more complex customizations and using the APIView seems like too much repetitive work?

From what I can gather, it’s more of a DRY situation than anything else. The fundamental cache functions are in the middleware, providing this convenience function really only allows that middleware to be applied and used on a per-view basis.

Someone else would need to address that - I’ve only used DRF in the most primitive and fundamental situations. I’m looking at this from the perspective of a typical Django view, not accounting for any of the functionality added by the DRF views.