Подтвердить что ты не робот

Несколько декораторов для представления в Django: порядок выполнения

Я пытаюсь украсить представление 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?

4b9b3361

Ответ 1

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

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

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

изменить

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

Ответ 4

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

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