Несколько декораторов для представления в Django: порядок выполнения - PullRequest
31 голосов
/ 03 января 2012

Я пытаюсь украсить представление Django двумя декораторами, один для проверки входа в систему и один для проверки is_active.

Первый - встроенный @login_required, а второй -следующее:

def active_required(function):
    dec = user_passes_test(lambda u: u.is_active, '/notallowed', '')
    return dec(function)

Теперь декораторы в Python работают наизнанку, однако следующее не работает:

@active_required
@login_required
def foo(request):
    ...

Я хочу сначала проверить, вошел ли пользователь в систему, иперенаправить на страницу входа, если нет, и если он или она вошли в систему, я хочу проверить, активен ли он или она, и, если нет, выполнить перенаправление на '/notallowed'.

В результате происходит сбой login_required, пользователь не перенаправляется на страницу входа в систему, но выполняется @active_required, и, поскольку в этом случае пользователь имеет значение null, происходит сбой декоратора @active_required, и пользовательперенаправлен на /notallowed.

Изменение порядка, кажется, работает,

@login_required
@active_required
def foo(request):
    ...

но я подозреваю, что с этим подходом тоже что-то не так.

Что является правильнымспособ объединения двух декораторов, и почему порядок выполнения отличается от простых декораторов Python?

Ответы [ 4 ]

32 голосов
/ 03 января 2012

Теперь декораторы в Python работают наизнанку

Ну, я думаю, это зависит от вашего определения наизнанку.в вашем случае вы хотите, чтобы login_required выполнялся первым, и поэтому он должен быть «самым внешним» (верхним) декоратором

, как вы заметили, ваш последний пример работает и действительно является правильным способом сделать это

edit

возможно, путаница заключается в том, как (эти конкретные) декораторы работают

login_required(original_view) возвращает новое представление, которое сначала проверяет,вошел в систему, а затем вызывает original_view

так

login_required(
    active_required(
        my_view
    )
)

first checks if you are logged in, then
    first(second) checks if you are active, then
        runs my_vew
15 голосов
/ 03 января 2012

Декораторы применяются в порядке их появления в источнике. Итак, ваш второй пример:

@login_required
@active_required
def foo(request):
    ...

эквивалентно следующему:

def foo(request):
    ...
foo = login_required(active_required(foo))

Таким образом, если код одного декоратора зависит от чего-то, установленного (или обеспеченного) другим, вы должны поместить зависимый декоратор «внутри» декодендора.

Однако, как отмечает Крис Пратт, вам следует избегать зависимостей декоратора; при необходимости создайте один новый декоратор, который вызывает оба в правильном порядке.

11 голосов
/ 03 января 2012

На самом деле имеет смысл складывать декораторы только в том случае, если они обладают действительно уникальной функциональностью.Исходя из вашего описания, никогда не будет сценария, в котором вы захотите использовать active_required, но , а не login_required.Следовательно, имеет больше смысла иметь login_and_active_required декоратор, который проверяет и ветки, и ветви соответственно.Меньше печатать, меньше документировать и устраняет проблему.

4 голосов
/ 16 сентября 2013

Чтобы объяснить это немного подробнее (сначала я тоже был сбит с толку): active_required применяется первым в том смысле, что он берет my_view и оборачивает его в некоторый код. Затем login_required применяется и оборачивает результат в еще один код.

Но когда фактически вызывается эта обернутая версия my_view, сначала выполняется код, добавленный login_required (проверка того, что вы вошли в систему), затем выполняется код, добавленный active_required (проверка того, что вы активны) и затем наконец my_view выполняется.

...