djangodjango-rest-frameworkdjango-redis

Django Rest Framework: Pickle Response


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.


Solution

  • I solved this issue by having the view return a django.http.JsonResponse rather than a rest_framework.response.Response object. The problem is that the rest_framework Response 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. Since JsonResponse only deals with json, it doesn't have to go through this "content negotiation".