Можно ли использовать декораторы для проверки перед выполнением контроллера в Django
Что проверяют
Знаете ли вы штатные декораторы Django и понимаете ли, где проходит граница между декоратором, миксином и middleware. Следом просят написать свой декоратор и спрашивают, как повесить его на class-based view.
Как отвечать
Да. Роль контроллера в Django играет view, и декоратор оборачивает её так, что проверка выполняется до основного кода и может сразу вернуть ответ. Готовые варианты:
@login_required: анонима отправляет наsettings.LOGIN_URLс параметромnext;@permission_required("app.change_order", raise_exception=True): без права отдаёт 403 вместо редиректа;@user_passes_test(func): произвольное условие надrequest.user;@require_POST,@require_http_methods([...]): отсекают неподходящий HTTP-метод ответом 405.
Свой декоратор это функция, которая принимает view и возвращает обёртку _wrapped(request, *args, **kwargs), помеченную @functools.wraps(view). Внутри проверка и либо HttpResponseForbidden(), либо вызов исходной view.
Для class-based views декоратор функции напрямую не подходит. Вариантов два: @method_decorator(login_required, name="dispatch") на классе или миксины LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin, которые ставятся первыми в списке родителей.
Когда проверка нужна почти везде, декоратор на каждой view лишний. Для этого есть middleware, а с Django 5.1 появился штатный LoginRequiredMiddleware: он закрывает все view, а открытые помечаются @login_not_required. В DRF та же задача решается через permission_classes.
Где спотыкаются
Пишут обёртку без functools.wraps. View теряет имя и атрибуты, которые ставят другие декораторы, например csrf_exempt, и отладка превращается в угадывание.
Путают порядок. Декораторы применяются снизу вверх, а срабатывают сверху вниз, поэтому проверка аутентификации должна стоять выше проверок, которые обращаются к request.user.profile.
Считают декоратор полной защитой. Он знает про пользователя, но не про объект: @login_required пропустит к /orders/42/ любого вошедшего. Проверку владельца делают в запросе, например get_object_or_404(Order, pk=pk, user=request.user).