我对视图有十几个权限查询,确保用户具有在系统上执行某些操作的正确权限(即确保他们在正确的组中,如果他们可以编辑他们的个人资料,如果他们是小组管理员等。)
支票可能如下所示:
from django.contrib.auth.decorators import user_passes_test
test_canvote = lambda u: u.has_perm('polls.can_vote')
@user_passes_test(test_canvote)
def my_view(request):
# ...
这实际上是来自Django教程的代码(我有点丑陋)。有时检查是数据库密集型的,会触发多个查询。由于许多用户访问权限检查页面,事情很快就会变得很慢。
我的问题是,我可以(在您的帮助下)为user_passes_test装饰器构建一个包装器(或替换件),该装饰器在缓存中搜索键'TESTCACHE' + user.pk + 'testname'
,如果它不存在,则执行测试并保存结果。
我之前从未写过一个装饰器,但我想它看起来几乎与user_passes_test
相同,只是将测试作为字符串传递:
@cached_user_passes_test('test_canvote')
def my_view(request):
# ...
和往常一样,让我知道我是否疯了,或者Django是否已经为我做过这样的事情(所以我在其他地方遇到了问题)。
编辑:可在此处找到标准装饰器:http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/decorators.py
我认为替换user_passes_test
可能比包装它更容易,所以这里是起点。当然,如果您认为我在该声明中不正确,请告诉我:
try:
from functools import update_wrapper, wraps
except ImportError:
from django.utils.functional import update_wrapper, wraps # Python 2.3, 2.4 fallback.
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect
from django.utils.http import urlquote
from django.utils.decorators import auto_adapt_to_methods
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
def decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
path = urlquote(request.get_full_path())
tup = login_url, redirect_field_name, path
return HttpResponseRedirect('%s?%s=%s' % tup)
return wraps(view_func)(_wrapped_view)
return auto_adapt_to_methods(decorator)
答案 0 :(得分:1)
您可能需要序列化该函数(当我将其用作缓存的关键字时我没有这样做),但是这样的事情应该有效:
from django.core.cache import cache
def cached_user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
def decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
key = str(test_func) + str(request.user)
cached_test_result = cache.get(key)
if cached_test_result != None:
test_result = cached_test_result
else:
test_result = test_func(request.user)
cache.set(key, test_result, 60)
if test_result:
return view_func(request, *args, **kwargs)
path = urlquote(request.get_full_path())
tup = login_url, redirect_field_name, path
return HttpResponseRedirect('%s?%s=%s' % tup)
return wraps(view_func)(_wrapped_view)
return auto_adapt_to_methods(decorator)
答案 1 :(得分:0)
首先你可以写一下:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote')
def my_view(request):
# ...
其次,如果权限没有及时更改,您可以在会话中自由存储一些信息(我发现它比存储在任何类型的缓存中更方便),当用户登录时。
但请记住,如果您更改权限,则用户必须注销,然后重新使用新权限。