Привязка кэша Django на основе классов к ответу представления с параметром из запроса

Я хочу кэшировать ответ API и связать его с параметром, который присутствует в GET-запросе. Запрос выглядит следующим образом:

GET /products?producent=some_company

Вот мой упрощенный класс:

class ProductsListCreate(generics.ListCreateAPIView):

    def list(self, request, *args, **kwargs):
        producent = request.query_params.get("producent")
        cached_response = cache.get(f"response-products-{producent}", None)
        if not cached_response:
            queryset = self.filter_queryset(self.get_queryset())
            page = self.paginate_queryset(queryset)
            serializer = self.get_serializer(page)
            response = self.get_paginated_response(serializer.data)

            cache.set(f"response-products-{producent}", response , timeout=20)
            return response

        return cached_response 

Но когда я пытаюсь кэшировать ответ, я получаю ошибку:

django.template.response.ContentNotRenderedError: The response content must be rendered before it can be pickled.

Есть ли у вас какие-нибудь советы для меня? Я искал здесь https://docs.djangoproject.com/en/3.2/topics/cache/, когда пытался разобраться в этом. Сначала я попробовал подход с @cache_page, но он не позволяет мне использовать параметр из запроса, так что я думаю, что путь к этому лежит через Low level cache API.

Сообщение об ошибке на самом деле довольно описательно. Вы должны вызвать render() на ответе, прежде чем пытаться собрать его в кэшируемый формат. Проблема в том, что это приведет вас к кроличьей норе, в которой придется деконструировать ответ и восстанавливать его из кэшированных данных. ИМО, лучше кэшировать набор запросов. Тем не менее, вот как это будет выглядеть:

class ProductsListCreate(generics.ListCreateAPIView):

    def list(self, request, *args, **kwargs):
        producent = request.query_params.get("producent")
        response_triple = cache.get(f"response-products-{producent}", None)
        if not response_triple:
            queryset = self.filter_queryset(self.get_queryset())
            page = self.paginate_queryset(queryset)
            serializer = self.get_serializer(page)
            response = self.get_paginated_response(serializer.data)

            response.render()

            if not response.status_code >= 400:
                # django 3.0 has no .items() method, django 3.2 has no ._headers
                if hasattr(response, '_headers'):
                    headers = response._headers.copy()
                else:
                    headers = {k: (k, v) for k, v in response.items()}
                response_triple = (
                    response.rendered_content,
                    response.status_code,
                    headers
                )
                cache.set(f"response-products-{producent}", response_triple, timeout=20)
        else:
            # build smaller Django HttpResponse
            content, status, headers = response_triple
            response = HttpResponse(content=content, status=status)
            for k, v in headers.values():
                response[k] = v
        if not hasattr(response, '_closable_objects'):
            response._closable_objects = []

        return response

Взято из: https://github.com/chibisov/drf-extensions/blob/ecdf3a95d7f18ccf9cffa55809635c3715179605/rest_framework_extensions/cache/decorators.py#L63

Вернуться на верх