Почему встроенные классы разрешений 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

Как видите, доступ к свойству запускает всю логику аутентификации, которая необходима для работы следующей проверки разрешений.

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