Джанго Динамический дизайн меню вопрос - PullRequest
1 голос
/ 20 декабря 2009

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

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

Но проблема в том, что по соображениям безопасности я тоже хочу ограничить доступ к представлениям. Фрагмент, который я нашел в документации , выглядит следующим образом:

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 ]

2 голосов
/ 20 декабря 2009

РЕДАКТИРОВАТЬ: (исходный текст ответа с первоначальной, простой идеей см. В конце поста).

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

Подойдет ли вам этот тип шаблона?

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

Чтобы это работало на стороне Python, вы могли бы использовать RequestContext в своих представлениях с помощью обработчика пользовательских контекстовустановка переменной dyn_menu_items соответствующим образом.Если требуется некоторая справочная информация, глава Расширенные шаблоны Книги Джанго представляет 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 делает то, чтоВы хотите, но не достаточно СУХОЙ, как насчет того, чтобы свернуть свою собственную обертку:

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

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

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

1 голос
/ 02 января 2010

Я знаю, что этот вопрос задавался пару недель назад, но вы упомянули http://code.google.com/p/greatlemers-django-tools/ в одном из ваших комментариев, поэтому я подумал, что скину.

Проект все еще активен (хотя в настоящий момент он немного отошел на второй план), но я не уверен, что он такой же СУХОЙ, как и вы. Вам все равно придется указывать разрешения дважды, один раз в объекте модели для пункта меню и один раз в представлении. Однако это не обязательно плохо, так как права доступа, которые вы определяете в пункте меню, могут немного отличаться от прав доступа в представлении.

Если вы хотите сделать все в одном месте, я бы, вероятно, предложил комбинацию служебной функции для использования в 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, но с добавленными параметрами perms и login_url (perms должен быть списком всех соответствующих).

# 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

...