在try-except中包装Python装饰器



我正在使用Redis在Django Rest Ramework API中缓存一些视图。假设我有以下观点:

from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from rest_framework.response import Response
from rest_framework.views import APIView
class MyView(APIView):
@method_decorator(cache_page(60 * 15))
def get(self, request):
return Response({"timestamp": timezone.now()})

当Redis启动并运行时,一切都很好。然而,我们的Redis实例最近出现了间歇性中断(大喊Heroku Redis(,这导致所有使用Redis缓存的端点崩溃,从而导致任何使用cache_page装饰器的端点崩溃并返回500内部服务器错误。

我想实现一种故障转移机制,该机制只需忽略缓存,并在引发redis.ConnectionError时成功返回响应。以下是我目前所拥有的:

def cache_page_with_failover(timeout, *, cache=None, key_prefix=None):
def decorator(view_func):
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return cache_page(timeout, cache=cache, key_prefix=key_prefix)(
view_func
)(*args, **kwargs)
except redis.ConnectionError:
return view_func(*args, **kwargs)
return wrapper
return decorator

用法:

class MyView(APIView):
@method_decorator(cache_page_with_failover(60 * 15))
def get(self, request):
return Response({"timestamp": timezone.now()})

这很有效,但它非常冗长,我觉得我在复制cache_page签名。有没有更干净、更优雅的写法?

直接接受cache_page:返回的decorator

def with_redis_failover(cache_decorator):
def decorator(view_func):
cache_decorated_view_func = cache_decorator(view_func)
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return cache_decorated_view_func(*args, **kwargs)
except redis.ConnectionError:
return view_func(*args, **kwargs)
return wrapper
return decorator

用法:

# @method_decorator(cache_page_with_failover(60 * 15))
@method_decorator(with_redis_failover(cache_page(60 * 15)))

这可以概括为:

def with_failover(decorator, exception_type):
def _decorator(view_func):
decorated_view_func = decorator(view_func)
@wraps(view_func)
def wrapper(*args, **kwargs):
try:
return decorated_view_func(*args, **kwargs)
except exception_type:
return view_func(*args, **kwargs)
return wrapper
return _decorator

用法:

@method_decorator(with_failover(cache_page(60 * 15), redis.ConnectionError))
with_redis_failover = partial(with_failover, exception_type=redis.ConnectionError)
@method_decorator(with_redis_failover(cache_page(60 * 15)))

最新更新