Привязка кэша 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