Django, все представления требуют логина, кроме украшенных представлений `@login_not_required`

Django ориентирован на разрешение доступа всем большую часть времени, и ограничение доступа в исключительных случаях.

Я делаю сайт, где я хотел бы, чтобы доступ был ограничен большую часть времени, а в редких случаях был открыт для всех. Убедиться в том, что ко всем представлениям добавлено украшение @login_required, чревато ошибками.

Я добился этого, создав пользовательское промежуточное ПО следующим образом. Для отслеживания открытых URL я определил список, а затем, когда я хотел открыть URL, я добавлял его в список, а промежуточное ПО проверяло путь запроса по этому списку и соответственно разрешало исключения.

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

В конечном итоге я хотел бы создать свой собственный @login_not_requied декоратор. Как получить основанный на классе вид или функцию, которую запрос собирается вызвать в промежуточном ПО, чтобы я мог проверить, не требует ли вид входа?

Сначала создайте декоратор:

from functools import wraps

def login_not_required(obj):
    """Adds the attrbiute login_not_required = True to the object (func/class).

    Use it as follows:
        @login_not_required
        class FooView(generic.View):
            ...

        @login_not_required
        def bar_view(request):
            ...
    """
    @wraps(obj)
    def decorator():
        obj.login_not_required = True
        return obj
    return decorator()

Затем создайте свое собственное промежуточное ПО:

from contextlib import suppress

from django.conf import settings
from django.shortcuts import redirect

NONE_AUTH_ACCOUNT_PATHS = [
    settings.STATIC_URL,
    '/accounts/login/',
    '/accounts/password_reset/',
    '/accounts/reset/',
    '/favicon.ico',
    '/terminal/login/',
    '/terminal/login_failed/',
]


class RequireLoginCheck:
    """Middleware to require authentication on all views by default, except when allowed.

    URLS can be opened by adding them to NONE_AUTH_ACCOUNT_PATHS, or by adding
    the @login_not_required decorator.

    Must appear below the sessions middleware because the sessions middleware
    adds the user to the request, which is used by this middleware.
    """

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def _is_none_auth_path(self, path):
        for none_auth_path in NONE_AUTH_ACCOUNT_PATHS:
            if path.startswith(none_auth_path):
                return True
        return False

    def _is_login_not_required(self, view_func):
        with suppress(AttributeError):
            # If a class with the @login_not_required decorator, will return True
            return view_func.view_class.login_not_required
        with suppress(AttributeError):
            # If a function with the @login_not_required decorator, will return True
            return view_func.login_not_required
        return False

    def process_view(self, request, view_func, view_args, view_kwargs):
        """https://docs.djangoproject.com/en/stable/topics/http/middleware/#other-middleware-hooks"""
        if not (
            request.user.is_authenticated
            or self._is_login_not_required(view_func)
            or self._is_none_auth_path(request.path)
        ):
            if settings.LOGIN_URL != request.path:
                # if next URL after login is the same login URL, then cyclic loop
                return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
            else:
                return redirect('%s?next=%s' % (settings.LOGIN_URL, '/'))
        return None

Убедитесь, что вы добавили свое промежуточное ПО в MIDDLEWARE ниже промежуточного ПО сессий, в settings.py! Если у вас есть способы улучшить его, пожалуйста, прокомментируйте ниже, и мы сделаем его лучше для всех.

Вернуться на верх