2009-12-20 1 views
1

Я хочу создать динамическое меню в соответствии с разрешениями пользователей. Как уже обсуждалось here и по в documentation itself, я знаю, что я могу добиться этого в шаблонах, используя следующий фрагмент кода:Django Dynamic design design question

{% 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) 

Разве это не против СУХОЙ принципе? Разве нет способа определить только в одном месте, какое разрешение необходимо для каждого URL-адреса? Возможно, в urls.py?

ответ

2

EDIT: (см. Конец поста для исходного текста ответа с исходной, простой идеей)

После любезно пострадавшего с cluebat (см комментарий Ора в ниже), я нахожу, может видеть больше проблемы, чем раньше. Извините, так долго. Во всяком случае:

Будет ли этот шаблон подходящим для вас?

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

Чтобы сделать эту работу на стороне Python, вы можете использовать RequestContext в ваших взглядах с пользовательской настройки контекста переменной dyn_menu_items соответствующим процессором. В этом случае требуется некоторая справочная информация, то Advanced Templates глава Джанго книги вводит RequestContext, показывает, как использовать его с render_to_response (любопытное важных :-)) и т.д.

Кроме того, я думаю, на данный момент это может быть полезно положить функции представления, ответственные за блокированные разделы вашего сайта в списке где-то:

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

Тогда вы могли бы map пар функций, скажете prepare_pattern и prepare_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), который будет использоваться в вашем URLconf.

Я надеюсь, что это имеет смысл и некоторую помощь ...

скомпонованной ОТВЕТ:

на основании краткого описания, я не уверен, что решение будет лучше для вы ... Но если permission_required сниппает делает то, что вы хотите, просто не DRY-LY достаточно, как насчет прокатки своей собственной обертки:

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

вы можете поместить это в любом месте, в том числе в привязках.Затем вы можете заменить все упоминания '/loginpage/' ссылкой на переменную, определенную в верхней части вашего файла URL-адресов, и у вас будет решение с единственным упоминанием фактического URL-адреса входа, для одноразового обновления указанного URL-адреса следует вам нужно переместить его. :-)

Конечно, взгляды все равно должны быть обернуты явно; если это вас беспокоит, вы можете попытаться сделать ask_to_login в декоратор для удобства упаковки на сайте определения. (Но, возможно, лучше не делать этого, чтобы вы не заставили себя выкопать свои взгляды из-под декоратора, если они вам понадобятся в какой-то момент в будущем.)

+0

Спасибо, но я искал что-то «сушилку», как http://code.google.com/p/greatlemers-django-tools/, но я не знаю, если проект все еще активен. – jbochi

+0

Да, я могу понять, что вы имеете в виду сейчас ... Я отредактировал свой ответ, чтобы включить эскиз возможного решения, интересно, может ли это использовать вас? –

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, позволяя ему проверять эти базовые разрешения, если они существуют, и затем проверять добавленные дополнения.

Надеюсь, это поможет.

-

G