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
!
Если у вас есть способы улучшить его, пожалуйста, прокомментируйте ниже, и мы сделаем его лучше для всех.