Несколько декораторов для представления в 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?
Ответы
Ответ 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
.