You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
flyapps/fir_ser/api/utils/decorators.py

131 lines
4.1 KiB

from functools import wraps, WRAPPER_ASSIGNMENTS
from django.http.response import HttpResponse
def get_cache(alias):
from django.core.cache import caches
return caches[alias]
class CacheResponse:
"""
Store/Receive and return cached `HttpResponse` based on DRF response.
.. note::
This decorator will render and discard the original DRF response in
favor of Django's `HttpResponse`. The allows the cache to retain a
smaller memory footprint and eliminates the need to re-render
responses on each request. Furthermore it eliminates the risk for users
to unknowingly cache whole Serializers and QuerySets.
"""
def __init__(self,
timeout=None,
key_func=None,
cache=None,
cache_errors=None):
if timeout is None:
self.timeout = None
else:
self.timeout = timeout
if key_func is None:
self.key_func = ''
else:
self.key_func = key_func
if cache_errors is None:
self.cache_errors = True
else:
self.cache_errors = cache_errors
self.cache = get_cache(cache or 'default')
def __call__(self, func):
this = self
@wraps(func, assigned=WRAPPER_ASSIGNMENTS)
def inner(self, request, *args, **kwargs):
return this.process_cache_response(
view_instance=self,
view_method=func,
request=request,
args=args,
kwargs=kwargs,
)
return inner
def process_cache_response(self,
view_instance,
view_method,
request,
args,
kwargs):
key = self.calculate_key(
view_instance=view_instance,
view_method=view_method,
request=request,
args=args,
kwargs=kwargs
)
timeout = self.calculate_timeout(view_instance=view_instance)
response_triple = self.cache.get(key)
if not response_triple:
# render response to create and cache the content byte string
response = view_method(view_instance, request, *args, **kwargs)
response = view_instance.finalize_response(request, response, *args, **kwargs)
response.render()
if not response.status_code >= 400 or self.cache_errors:
# django 3.0 has not .items() method, django 3.2 has not ._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
)
self.cache.set(key, response_triple, timeout)
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
def calculate_key(self,
view_instance,
view_method,
request,
args,
kwargs):
if isinstance(self.key_func, str):
key_func = getattr(view_instance, self.key_func)
else:
key_func = self.key_func
return key_func(
view_instance=view_instance,
view_method=view_method,
request=request,
args=args,
kwargs=kwargs,
)
def calculate_timeout(self, view_instance, **_):
if isinstance(self.timeout, str):
self.timeout = getattr(view_instance, self.timeout)
return self.timeout
cache_response = CacheResponse