What I’m trying to do is build a custom version of cache_page
where I have more control over the cache key, but I’m getting stuck with even the basic caching of my response:
from django.core.cache import cache
from rest_framework import viewsets
from rest_framework.response import Response
from app import models
class BaseViewSet(viewsets.GenericViewSet):
queryset = models.Items.objects.all()
def get_queryset(self):
return models.Items.objects.all()
def list(self, request, **kwargs):
response = Response({})
cache.set('test', response, 10)
return response
Where the relevant parts of my settings.py
are setup as:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://127.0.0.1:6729/1",
},
}
When I try to call the endpoint I get:
django.template.response.ContentNotRenderedError: The response content must be rendered before it can be pickled.
Then if I change the line to:
cache.set('test', response.render(), 10)
I get:
AssertionError: .accepted_renderer not set on Response
(If I set the renderer it complains about the accepted media, then the context and finally fails with TypeError: 'bytes' object is not callable
)
Despite the fact that the API call itself works fine without the caching.
cache_page
actually works fine, so I know it’s possible to cache the response, but I can’t figure out what I’m missing.
2
Answers
I solved this issue by having the view return a
django.http.JsonResponse
rather than arest_framework.response.Response
object. The problem is that the rest_frameworkResponse
is mediatype agnostic, and as far as I can tell relies on having the renderer and accepted media values set further down the call chain of the view, after it has left your handler. SinceJsonResponse
only deals with json, it doesn’t have to go through this "content negotiation".cache_page
work because of this: