2009-12-20 60 views
1

我想根據用戶權限創建動態菜單。正如已經討論here並通過了documentation itself,我知道我可以使用下面的代碼片段實現這一目標的模板:Django動態菜單設計問題

{% if perms.polls.can_vote %} 
    <li> 
     <a href="/polls/vote">Vote</a> 
    </li> 
{% endif %} 

但問題是,出於安全原因,我想限制訪問的意見太。我在documentation發現該片段如下:

from django.contrib.auth.decorators import permission_required 

def my_view(request): 
    # ... 
my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view) 

這難道不是對DRY原則?沒有辦法只在一個地方定義每個url需要什麼權限?也許在urls.py?

回答

2

編輯:(參見後結束與最初的,簡單的想法的回答原文)

與cluebat(請參見下面的OP的評論)被親切災區後,我發現我可以看到比以前更多的問題。對不起,花了這麼長時間。無論如何:

請問這種模板適合你嗎?

{% for mi in dyn_menu_items %} 
    {% if mi.authorised %} 
    <a href={{ mi.url }}>{{ mi.title }}</a> 
    {% endif %} 
{% endfor %} 

要在Python端這項工作,你可以用你的看法RequestContext帶有自定義背景處理器適當設置dyn_menu_items變量。在情況下,需要一些背景信息,Django的書的Advanced Templates章介紹RequestContext,顯示瞭如何render_to_response(還挺重要:-))使用等

另外,我想在這一點上它可能是有用的把負責網站的鎖定了部分視圖功能列表中的某個地方:

_dyn_menu_items = [(url1, view1, title1, perm1), ...] 

然後,你可以map一對夫婦的功能,說prepare_patternprepare_menu_item跨名單,有它大致的工作就像這樣:

def prepare_pattern(menu_item): 
    url1, view, title, perm = menu_item 
    pattern = PREPARE_URLCONF_ENTRY_SOMEHOW(...) # fill in as appropriate 
    return pattern 

def prepare_menu_item(menu_item): 
    url, view, title, perm = menu_item 
    mi = PREPARE_THE_BIT_FOR_REQUESTCONTEXT(...) # as above 
    return mi 

這些可以合併成一個單一的功能,當然,但不是每個人都會發現結果更具可讀性......無論如何,map(prepare_menu_item, _dyn_menu_items)的輸出將需要一個字典,通過有用的信息傳遞給你的意見上下文處理器(找出其中,這是稍微乏味的一點,我會留給你;-)),而輸出map(prepare_pattern, _dyn_menu_items),我們稱之爲dyn_menu_patterns,將用於patterns('', *dyn_menu_patterns),用於你的URL配置。

我希望這是有道理的,是有一定的幫助......

預編輯答案:

根據您的簡短說明,我不知道有什麼解決辦法將是最適合你......但如果permission_required片段你想要做什麼,只是不DRY-LY不夠,如何使用您自己的包裝:

def ask_to_login(perm, view): 
    return permission_required(perm, login_url='/loginpage/', view) 

你可以把這個在任何地方,包括在URL配置。然後,您可以將所有提及的'/loginpage/'替換爲引用您的URL文件頂部定義的變量,並且您可以爲自己提供一個解決方案,只需提及實際的登錄URL即可,對於所述URL的一次更新你必須移動它。 :-)

當然,視圖仍然需要明確包裝;如果這讓你感到困擾,你可以嘗試將ask_to_login變成裝飾器,以便在定義站點上輕鬆打包。 (但也許最好不要這樣做,否則你會強迫自己從裝飾者那裏挖掘你的觀點,以防將來在某些時候需要它們未裝飾)。

+0

謝謝,但我一直在尋找一些「機」之類http://code.google.com/p/greatlemers-django-tools/,但我不知道如果項目仍然活躍。 – jbochi 2009-12-20 19:07:20

+0

是的,我現在可以看到你的意思了......我編輯了我的答案,包括一個可能的解決方案的草圖,我想知道這對你有用嗎? – 2009-12-20 22:13:34

1

我知道這個問題被問到幾個星期前現在,但你提到http://code.google.com/p/greatlemers-django-tools/在你的意見之一,所以我想我會芯片英寸

該項目仍然活躍(雖然現在稍微在backburner),但我不知道如果它跟以前一樣幹。您仍然必須指定權限兩次,一次在菜單項的模型對象中,一次在視圖中。這不一定是件壞事,因爲您在菜單項上定義的權限可能與視圖上的權限稍有不同。

如果你想在一個地方做所有事情,我可能會建議一個用於urls.py的實用函數的組合,它可以爲視圖添加限制,同時也可以在某處存儲所述限制以便與特殊模板標記一起使用。我想可能看起來像這樣。

# Stored in a file named access_check_utils.py say. 
from django.conf.urls.defaults import url 
from django.core.urlresolvers import get_callable 
from django.contrib.auth.decorators import permission_required 

access_checked_urls = {} 

def access_checked_url(regex, view, kwargs=None, name=None, prefix='', perms=None, login_url=None): 
    if perms is None: 
     perms = [] 
    callback = None 
    if callable(view): 
     callback = view 
    elif isinstance(view, basestring): 
     if prefix: 
      view_path = "%s.%s" % (prefix, view) 
     else: 
      view_path = view 
     try: 
      callback = get_callable(view_path) 
     except: 
      callback = None 
    if callback is not None: 
     # Add all the permissions 
     for perm in perms: 
      callback = permission_required(perm, login_url=login_url)(callback) 
     if name is not None: 
      access_checked_urls[name] = perms 
    else: 
     callback = view 
    return url(regex, callback, kwargs=kwargs, name=name, prefix=prefix) 

這應該在urls.py需要的坑叫工作以同樣的方式,你會與一個正常的URL,但與添加的燙髮和LOGIN_URL參數(燙髮應該是所有相關的人的名單)。

# In a templatetag folder somewhere 
from django import template 
from django.core.urlresolvers import 

# This needs to point to the right place. 
from access_check_utils import access_checked_urls 

register = template.Library() 

@register.inclusion_tag("access_checked_link.html", takes_context=True) 
def access_checked_link(context, title, url, *args, **kwargs): 
    perms = access_checked_urls.get(url, []) 
    if not perms: 
     allowed = True 
    else: 
     allowed = context.request.user.has_perms(perms) 
    return { 'allowed': allowed, 
      'url': reverse(url, *args, **kwargs), 
      'title': title } 

這將有一個像相關的模板文件:

{% if allowed %}<a href="{{ url }}">{{ title }}</a>{% endif %} 

我還沒有完全測試這一點,但它應該工作(或者至少是一件應該工作的良好基礎)。我甚至可能會考慮在gdt_nav中添加這樣的內容,如果它們存在,可以檢查這些基本權限,然後檢查是否添加了其他附加組件。

希望這有一些幫助。

-