2010-01-18 90 views
5

我在視圖上有十幾個權限查找,確保用戶擁有在系統上執行某些操作的權限(例如,如果他們可以編輯其配置文件,請確保它們位於正確的組中,如果他們是組管理員等)。緩存非查看返回

的檢查可能是這樣的:

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) 

回答

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) 
+0

由於顯而易見的原因,測試函數的參數也需要成爲關鍵的一部分。 – Oli 2010-01-18 21:20:45

+0

當然,試圖與最後一個太聰明...編輯來做裝飾器內部的獲取和設置。這會工作嗎? – ara818 2010-01-19 16:01:05

0

首先,你可以簡單的寫:

from django.contrib.auth.decorators import permission_required 

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

其次,如果權限不隨時間改變,你可以自由地存儲一些信息在會話(我發現它更方便而不是存儲在任何類型的緩存中)。

但請記住,如果您更改了權限,則用戶必須註銷並返回以使用新的權限。

+0

我想你已經錯過了這一點。正如我所說的,我的測試比簡單的'permission_required'稍微複雜一點,都在'user_passes_test'上。 「我如何緩存裝飾器?」可能是一個更準確的標題。這更關於你如何做緩存。 – Oli 2010-01-20 12:03:18