Почему встроенные классы разрешений Django REST Framework имеют проверку request.user?
Я пишу свой первый проект DRF, и меня заинтересовало, как реализованы встроенные классы разрешений DRF - IsAuthenticated и IsAdminUser, например.
В rest_framework/permissions.py вы можете найти следующий код:
class IsAdminUser(BasePermission):
...
def has_permission(self, request, view):
return bool(request.user and request.user.is_staff)
class IsAuthenticated(BasePermission):
...
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
Как видите, оба класса проверяют наличие request.user перед вторым условием.
Возможно, это необходимо по какой-то причине, но я не могу понять, почему.
Первое, что пришло мне в голову, это то, что объект request.user может иметь значение None или иметь какой-то объект без атрибутов is_staff или is_authenticated, и в этом случае request.user.is_staff и request.user.is_authenticated вызовут AttributeError.
Я немного поэкспериментировал, и даже если я посылаю неаутентифицированный запрос, request.user указывает на AnonymousUser. То же самое написано в документации:
Если ни один класс не аутентифицируется, request.user будет установлен в экземпляр django.contrib.auth.models.AnonymousUser, а request.auth будет установлен на None.
Но самое интересное здесь то, что AnonymousUser объект
на самом деле имеет атрибуты is_staff и is_authenticated! Оба они установлены в
False.
Так почему же вы сначала проверяете наличие request.user, а не проверяете напрямую условия request.user.is_staff и request.user.is_authenticated?
request.user - это способ запустить всю логику аутентификации в django-rest-framework. Это ленивое свойство, доступ к которому должен быть получен до появления таких атрибутов, как request.user.is_authenticated.
Релевантный код в django-rest-framework:
class Request:
…
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
Как видите, доступ к свойству запускает всю логику аутентификации, которая необходима для работы следующей проверки разрешений.