缓存非视图返回

发布于 2024-08-18 02:12:32 字数 2355 浏览 8 评论 0原文

我对视图进行了十几个左右的权限查找,以确保用户拥有在系统上执行某些操作的正确权限(即确保他们位于正确的组中,如果他们可以编辑他们的个人资料,如果他们是组管理员, ETC)。

检查可能如下所示:

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)

I have a dozen or so permission lookups on views that make sure users have the right permissions to do something on the system (ie make sure they're in the right group, if they can edit their profile, if they're group administrators, etc).

A check might look like this:

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):
    # ...

This is actually code from the Django tutorial (mine is a little uglier). Sometimes a check is very database intensive, firing off multiple queries. With lots of users hitting permission-checked pages, things can quickly get quite slow.

My question is, can I (with your help) build a wrapper (or replacement) for the user_passes_test decorator that searches the cache for a key 'TESTCACHE' + user.pk + 'testname' and if it doesn't exist, executes the test and saves its result.

I've never written a decorator before but I imagine it would look nearly identical to the user_passes_test one, just passing the test as a string:

@cached_user_passes_test('test_canvote')
def my_view(request):
   # ...

And as ever, let me know if I'm mad or if Django already does this for me (so I've problems elsewhere).

Edit: The standard decorators can be found here: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/decorators.py

I think it might be easier replacing user_passes_test than wrapping it so here's the starting point. Of course, if you feel I'm incorrect in that statement, let me know:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

倾其所爱 2024-08-25 02:12:32

您可能需要序列化该函数(当我使用它作为缓存的密钥时我没有这样做),但类似这样的事情应该可以工作:

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)

You might need to serialize the function (which I'm not doing when I use it as the key to the cache), but something like this should work:

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)
抚你发端 2024-08-25 02:12:32

首先你可以简单地写:

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote')
def my_view(request):
    # ...

其次,如果权限没有及时改变,当用户登录时,你可以自由地在会话中存储一些信息(我发现它比存储在任何类型的缓存中更方便)但

请记住,如果您更改权限,用户必须注销并重新登录才能使用新权限。

First you can simply write:

from django.contrib.auth.decorators import permission_required

@permission_required('polls.can_vote')
def my_view(request):
    # ...

Second, if the permissions don't change in time, you are free to store some info in the session (I found it more convenient, than storing in a cache of whatever type), when the user logs in.

But remember, that if you change the permissions, the user has to log out, and back in to work with the new permissions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文