我正在为Django REST API实现内容感知缓存系统。我想开发一个可以添加到现有视图中的组件,该组件可以通过检查缓存并在未命中时回到基类的行为来修改基类的行为。
基本上,我有这样的东西:
class Base:
def get(self, request, *args, **kwargs):
....
return Response
class AnotherBase:
def get(self, request, *args, **kwargs):
....
return Response
class Derived(Base):
pass
class OtherDerived(AnotherBase):
pass
我最初的想法是按照
class Cacheable:
def get(self, request, *args, **kwargs):
cache_key = self.get_cache_key(request)
base_get = #.... and this is the problem
return cache.get(cache_key, base_get(request, *args, **kwargs))
def get_cache_key(self, request):
# .... do stuff
class Derived(Cacheable, Base):
pass
class AnotherDerived(Cacheable, AnotherBase):
pass
很显然,这不起作用,因为我不知道如何,是否可能,或者是否建议从mixin访问同级超类。
我的目标是一个实现,使我可以在不影响现有类内部的情况下向现有视图添加缓存行为。
给定一个视图类C
,s.t。 C.get(request, *args, **kwargs) -> Response
,有一个功能,F
, F(C).get(...
在回退到C.get
之前进行缓存检查吗?在这种准形式表示中,我们将说在类定义中最左边的父类中添加一个mixin算作一个函数。
使用方法修饰符是否更合适?或类装饰器如何工作?
然后我在研究此内容时看到了对__metaclass__
的引用,但是我不清楚该方法的外观。
这是Python 3.6
答案 0 :(得分:1)
简单的例子:
def Base:
def _get_data(self):
# get the data eg from database
return self._get_data_native()
def get(self, request, *args, **kwargs):
return Response(self._get_data())
def Cacheable(Base):
def _get_data(self):
# get from cache ...
result = ...
if result is None:
# or from base ...
result = ...
return result
def Derived(Cacheable):
def _get_data_native(self):
# get the data eg from database
...
通过从Cacheable继承,您将缓存包括在此处,因为_get_data
在此处被覆盖。
对于这个问题,如果只想在一个地方添加缓存,则不需要元类或装饰器。
当然,可以使用装饰器以更通用的方式包括缓存。
例如参见以下答案:Is there a decorator to simply cache function return values?
答案 1 :(得分:0)
答案是装饰器和一些Django
特定的库。
from django.utils.decorators import method_decorator
from django.core.cache import cache
def cached_get(cache_key_func=None):
"""
Decorator to be applied via django.utils.decorators.method_decorator
Implements content-aware cache fetching by decorating the "get" method
on a django View
:param cache_key_func: a function of fn(request, *args, **kwargs) --> String
which determines the cache key for the request
"""
def decorator(func):
def cached_func(request, *args, **kwargs):
assert cache_key_func is not None, "cache_key_function is required"
key = cache_key_func(request, *args, **kwargs)
result = cache.get(key)
if result is None:
return func(request, *args, **kwargs)
return Response(result)
return cached_func
return decorator
@method_decorator(cached_get(cache_key_func=get_cache_key), name="get")
class SomeView(BaseView):
...
def get_cache_key(request):
# do arbitrary processing on request, the following is the naïve melody
key = urllib.urlencode(request.query_params)
return key
因此解决方案是使用Django内置的method_decorator
,将其第一个参数(装饰器)应用于以第二个参数name
命名的装饰类的方法到{{1} }。我定义了一个高阶函数method_decorator
,该函数将另一个函数作为参数,并返回一个curried函数(闭包,即所谓的闭包)。通过使用函数cached_get
(而不是,请注意,调用该函数)调用此函数,我得到了一个装饰器,该装饰器将应用于get_cache_key
上的'get'方法。
修饰器本身是一个简单的Python装饰器-在此应用程序中为SomeView
,原始的未经修饰的cached_func
方法为get
。因此,func
替换了cached_func
,因此,在调用SomeView.get
时,它首先检查高速缓存,但是在未命中时退回到未经修饰的方法。
我希望这种方法能够在通用性与内容感知密钥派生之间取得平衡。
答案 2 :(得分:-2)