Any good practice to caching ListAPIView?

963 views
Skip to first unread message

Fover

unread,
Mar 20, 2018, 8:33:33 AM3/20/18
to Django REST framework

I try to cache ListAPIView.I find solution on stackoverflow:

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

class ProductListAPIView(generics.ListAPIView):
    serializer_class = ProductSerializer

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

It works.
But in my project ,the client make GET request with timestamp, it will be cached , but other user can not hit this cache almost.

I try to remove timestamp, my code is:

class ProductListAPIView(generics.ListAPIView):
    serializer_class = ProductSerializer

    def dispatch(self, request, *args, **kwargs):
        key=gen_cache_key(request)
        print(key)
        res=cache.get(key)
        if res:
            return res
        res= super().dispatch(request, *args, **kwargs)
        cache.set(key,res,60)
        return res

def gen_cache_key(request):
    params=request.GET
    key=request.path
    for x in params.lists():
        if x[0]!='timestamp':
            key+=str(x[0])
            key+=str(x[1][0])
    return key

emmm. It raise an error. ContentNotRenderedError >> The response content must be rendered before it can be pickled.

How to deal with it ?

Fover

unread,
Mar 21, 2018, 8:34:56 AM3/21/18
to Django REST framework
I read django.core.cache's source code ,django handle cache on middleware , so I create a new middleware to deal this.

class DispatchMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if hasattr(response, 'cache_key'):
key = response.cache_key
timeout=response.cache_time
del response.cache_key
del response.cache_time
cache.set(key,response,timeout)
return response

and on dispatch method
def dispatch(self, request, *args, **kwargs):
key = gen_cache_key(request)
    res = cache.get(key)
if res:
return res
res = super().dispatch(request, *args, **kwargs)
res.cache_key = key
res.cache_time = 60 * 3
return res

add cache_key and cache_time attribute to response object.


Xavier Ordoquy

unread,
Mar 21, 2018, 8:41:52 AM3/21/18
to Django REST framework
Hi,

The message says that you are trying to pickle the response - which is done through the cache.set(key, res,60) call - while the response is not rendered yet.
This should be fixed by accessing response.content before calling the cache.set

Regards,
Xavier Ordoquy,
Linovia.

Fover

unread,
Mar 21, 2018, 9:51:48 PM3/21/18
to Django REST framework
On the dispatch method . I try to call response.content before cache it. It raise django.template.response.ContentNotRenderedError: The response content must be rendered before it can be accessed.

在 2018年3月21日星期三 UTC+8下午8:41:52,Xavier Ordoquy写道:

Daviddd

unread,
Mar 8, 2019, 3:42:48 AM3/8/19
to Django REST framework
I do something similar:

class CacheKeyDispatchMixin:
    def dispatch(self, *args, **kwargs):
        if self.request.method == 'GET' or self.request.method == 'HEAD':
            url_to_cache = '/{0}{1}'.format(get_language(), self.request.get_full_path())
            cache_hash = calculate_xxxhash(url_to_cache)
            data = cache.get(cache_hash)
            if not data:
                response = super(CacheKeyDispatchMixin, self).dispatch(*args, **kwargs)
                if response.status_code == 200:
                    response.render()
                    cache.set(cache_hash, response)
                    logger.info('Cache added {0} ({1})'.format(url_to_cache, cache_hash))
                return response
            logger.info('Cache hit {0} ({1}).'.format(url_to_cache, cache_hash))
            return data

        return super(CacheKeyDispatchMixin, self).dispatch(*args, **kwargs)



E.g.

class MyViewSet(CacheKeyDispatchMixin, ViewSet):
Reply all
Reply to author
Forward
0 new messages