Может ли шаблон django знать, имеет ли представление, из которого он вызывается, декоратор @login_required?

Допустим, у меня есть система, в которой есть некоторые страницы, которые являются общедоступными (могут просматривать как неаутентифицированные пользователи, так и вошедшие в систему пользователи), и другие, которые могут просматривать только вошедшие в систему пользователи.

Я хочу, чтобы шаблон отображал немного разное содержимое для каждого из этих двух классов страниц. Декоратор представления @login_required всегда используется для представлений, которые могут просматривать только зарегистрированные пользователи. Однако мой шаблон должен знать, используется ли этот декоратор в представлении, из которого был вызван шаблон.

Пожалуйста, имейте в виду, что мне все равно, вошел ли пользователь в систему или нет для общедоступных страниц. Что меня волнует, так это то, может ли страница быть просмотрена широкой публикой, и отсутствие декоратора @login_required скажет мне об этом.

Может ли кто-нибудь подсказать мне, как шаблон узнает, используется ли конкретный декоратор в представлении, из которого вызывается шаблон?


person Krystian Cybulski    schedule 29.03.2009    source источник


Ответы (4)


Да, это возможно, но не очень прямолинейно. Усложняющим фактором является то, что декоратор Django login_required на самом деле проходит через 2 уровня косвенности (одна динамическая функция и еще один декоратор), чтобы в итоге оказаться в django.contrib.auth.decorators._CheckLogin, который является классом с методом __call__.

Допустим, у вас есть не-django, декорированная функция, которая выглядит так:

def my_decorator(func):
    def inner():
        return func()
    return inner

@my_decorator
def foo():
    print foo.func_name

# results in: inner

Проверить, была ли функция foo обернута, можно так же просто, как проверить имя объекта функции. Вы можете сделать это внутри функции. Имя на самом деле будет именем последней функции-оболочки. В более сложных случаях вы можете использовать модуль inspect для просмотра внешних фреймов от текущего фрейма, если вы ищете что-то конкретное.

Однако в случае Django тот факт, что декоратор на самом деле является экземпляром класса _CheckLogin, означает, что функция на самом деле не является функцией и, следовательно, не имеет свойства func_name: попытка выполнения приведенного выше кода вызовет исключение.

Однако просмотр исходного кода для django.contrib.auth.decorators._CheckLogin показывает, что экземпляр _CheckLogin будет иметь свойство login_url. Это довольно простая вещь для проверки:

@login_required
def my_view(request):
    is_private = hasattr(my_view, 'login_url')

Поскольку _CheckLogin также используется для реализации других декораторов аутентификации, этот подход также будет работать для permission_required и т. д. Однако у меня никогда не было необходимости использовать это, поэтому я действительно не могу комментировать, что вы должны искать, если у вас есть несколько декораторов вокруг одного представления ... упражнение, оставленное читателю, я думаю (проверить стек фреймов?).

Однако в качестве незапрошенного редакционного совета я бы сказал, что проверка самой функции, чтобы увидеть, была ли она обернута таким образом, кажется мне немного неудобной. Вы, вероятно, можете представить всевозможные непредсказуемые действия, ожидающие, когда новый разработчик придет в проект, как пощечину какому-то другому декоратору. На самом деле, вы также подвержены изменениям в самой среде django... риску безопасности, ожидающему своего часа.

По этой причине я бы рекомендовал подход Ван Гейла как нечто явное и, следовательно, гораздо более надежную реализацию.

person Jarret Hardie    schedule 29.03.2009
comment
Ваш первый блок кода не будет работать так, как написано. my_decorator аннулирует переданную ему функцию, поэтому помещать оператор печати в foo бесполезно. - person David Berger; 30.03.2009
comment
Спасибо, Дэвид, ты прав... Я пропустил первый блок, чтобы перейти к главному, не внимательно прочитав написанное. Оцените правку. - person Jarret Hardie; 30.03.2009
comment
Спасибо за подробный ответ. Ваш последний комментарий - это то, что я в итоге сделал, хотя я думаю, что может быть еще немного питонического; способ сделать с, не повторяясь. Я предполагаю, что в моем случае делать это правильно слишком сложно и не очень надежно. - person Krystian Cybulski; 30.03.2009
comment
Мне по-прежнему нужен способ получить ссылку на функцию представления из шаблона, но я предполагаю, что это можно сделать и каким-то окольным путем, используя собственный процессор в RequestContext. - person Krystian Cybulski; 30.03.2009

Я бы передал дополнительную переменную контекста в шаблон.

Таким образом, представление с @login_required будет передавать переменную типа private: True, а другие представления будут передавать private: False

person Van Gale    schedule 29.03.2009
comment
Я закончил тем, что сделал это в краткосрочной перспективе. Совершенно эффективный, но не совсем питонический, поскольку я мог получить это значение из-за отсутствия декоратора @login_required. - person Krystian Cybulski; 30.03.2009

Зачем вашему шаблону это знать? Если используется декоратор @login_required, само представление не позволяет людям, которые не вошли в систему, когда-либо открывать страницу и, следовательно, никогда не видеть шаблон с самого начала.

person Soviut    schedule 29.03.2009
comment
Вероятно, на шаблон ссылаются из нескольких мест, некоторые точки входа имеют @login_required, а некоторые нет. - person Blair Conrad; 29.03.2009
comment
Блэрд прав. Шаблон — это мой шаблон base.html, который является корневым родителем всех других шаблонов на моем веб-сайте. - person Krystian Cybulski; 30.03.2009

Шаблоны иерархичны, так почему бы не иметь версию @login_required и версию "без @login_required", обе из которых наследуются от одного и того же родителя?

Это сделало бы шаблоны намного чище и проще в обслуживании.

person Dave Webb    schedule 29.03.2009
comment
Есть тонкая диф. с тем, что я хочу сделать. Я не хочу проверять, на мой взгляд, используется ли конкретный шаблон, а не реализовывать поведение противоположного шаблона. - person Krystian Cybulski; 30.03.2009