Знание того, что происходит внутри Django REST Framework, когда пользователь запрашивает URL, поможет вам настроить его элементы с наименьшими усилиями и знать, какие методы переопределять и когда.
Существует большая вероятность, что вы столкнулись с необходимостью настроить функциональность вашего API в вашем предыдущем или текущем проекте. Возможно, вам пришлось проверить поле условно или показать / скрыть несколько полей в зависимости от типа пользователя? Или, может быть, вам нужно было реализовать разрешения пользователя на уровне объекта?
Очевидно, что невозможно поместить все в диаграмму, не сделав ее похожей на чертеж ракеты. Поэтому я опустил определенные части, которые вы вряд ли когда-либо переопределите, и сосредоточился на ключевых моментах, которые вы, скорее всего, будете настраивать.
Я также включил очень упрощенные шаги, которые выполняются до и после входа запроса в DRF и выхода из него для завершения цикла.
Имейте в виду, что в этой статье мы рассмотрим логический поток, основанный на ModelViewSet и общих представлениях, таких как ListAPIView, CreateAPIView и т.д., потому что люди используют их чаще всего. Следовательно, логика сериализатора основана на ModelSerializer классе, поскольку он используется этими классами просмотра.
ПРИМЕЧАНИЕ: Даже если вы создаете свои пользовательские классы поверх обычного APIView, GenericAPIView или базового Serializer, вы должны стараться повторно использовать код из их нисходящих классов, когда это возможно, поскольку они предлагают надежные реализации и хорошие методы программирования.
Теперь давайте рассмотрим процесс с некоторыми дополнительными деталями.
Что происходит до того, как запрос пользователя достигает DRF
- Все начинается с того, что пользователь запрашивает URL-адрес API.
- Веб-сервер (например, Apache или Nginx) получает запрос и пересылает его на сервер приложений.
- Сервер приложений (например, WSGI и, в большинстве случаев, Gunicorn) создает экземпляр приложения Django и пересылает ему запрос.
- Django передает запрос через свое промежуточное программное обеспечение (middleware).
- Django выполняет поиск подходящего представления по зарегистрированным маршрутам. Здесь вступают в игру шаблоны URL из нашего файла urls.py.
- Если подходящее представление найдено, Django вызывает свой dispatch() метод и передает ему объект запроса в качестве аргумента. Метод отправки совершает все волшебство, как вы видели из диаграммы.
- Если подходящего представления не существует, приложение Django возвращает ошибку 404.
Что происходит внутри DRF во время запроса, шаг за шагом
Поскольку этот процесс довольно детализирован, я почувствовал, что разбиение его на этапы имело бы смысл.
Шаг 1: Инициализация запроса
dispatch() Метод переносит стандартный Django HttpRequest в специальный объект запроса DRF.
Объект запроса DRF принимает список анализаторов и список аутентификаторов в качестве аргументов, так что именно здесь они настраиваются.
Синтаксические анализаторы исходят из parser_classes объявленных в представлении или DEFAULT_PARSER_CLASSES определенных в настройках. Запрос DRF лениво анализирует данные, т. е. он оценивает анализаторы только при обращении к его .data, .POST и .FILES свойствам.
Средства аутентификации исходят из authentication_classes объявленных в представлении или DEFAULT_AUTHENTICATION_CLASSES определенных в настройках. Аналогично, запрос DRF проверяет подлинность пользователя лениво, т. е. только при обращении к его .user свойству.
Шаг 2: Аутентификация пользователя
dispatch() Метод пытается аутентифицировать пользователя путем вызова perform_authentication() метода.
Под капотом он просто вызывает request.user упомянутый выше.
Базовая логика перебирает каждый класс аутентификации и пытается идентифицировать пользователя. Если ни один из них не завершается успешно, пользователь устанавливается в качестве AnonymousUser экземпляра.
Шаг 3: Проверены разрешения
dispatch() Метод проверяет разрешения пользователя путем вызова check_permissions() метода.
Здесь оцениваются permission_classes объявленные в представлении или DEFAULT_PERMISSION_CLASSES определенные в настройках.
Классы разрешений обычно рассматривают метод запроса, т.е. GET, POST DELETE и т.д., и определяют, имеет ли пользователь право запросить его. Соответственно, они возвращают True или False.
Если возвращается какой-либо из классов разрешений, Falseметод check_permissions() вызывает одно из двух исключений:
- NotAuthenticated исключение (401) для анонимных пользователей
- PermissionDenied исключение (403) во всех остальных случаях
Всякий раз, когда возникает исключение, dispatch() оно перехватывается, оборачивается в Response экземпляр и передается вверх по потоку. В конце концов, это отображается пользователю как ответ на ошибку.
Шаг 4: выбран обработчик
dispatch() Метод выполняет поиск соответствующего обработчика для метода запроса.
Здесь вызываются обработчики list(), create() retrieve() update() и destroy(), то есть те, которые вы, скорее всего, переопределите
Если не найден подходящий обработчик, dispatch() возникает MethodNotAllowed исключение (405).
Шаг 5: получен набор запросов или объект
Метод обработчика вызывает get_queryset() и / или get_object() методы для извлечения запрошенных объектов модели.
Обратите внимание, что ни один из этих методов не вызывается в create(), поскольку они не нужны для создания новых объектов.
Также обратите внимание, что get_object() метод вызывается только на подробных маршрутах, таких как retrieve(), update() и destroy(), где задан параметр PK.
Наконец, вы должны знать, что get_object() также проверяет разрешения пользователя на объект, вызывая check_object_permissions() метод.
Шаг 6: создан экземпляр сериализатора
Обработчик создает экземпляр ModelSerializer класса и передает ему данные запроса и / или объект модели. На этом этапе проверка или сериализация не выполняются, потому что DRF выполняет их лениво.
Шаг 7: Выполняется проверка
Обработчик вызывает is_valid(raise_exception=True) метод сериализатора. Обратите внимание, что он вызывается только в create(), update() и partial_update(), поскольку другие действия не принимают никаких входящих данных.
Внутри is_valid() метода происходит несколько вещей:
- Необработанные данные преобразуются в типы данных Python с помощью to_internal_value() метода, который также запускает проверку модели и поля. Если в поле возникают ошибки (например, обязательное поле пусто), будет вызвано исключение, и его пользовательская проверка не будет запущена.
- После преобразования данных поля логика проверяет, существует ли пользовательский метод проверки, и запускает его, если это так. Здесь вступают в игру ваши пользовательские методы, названные как validate_field_name().
- Наконец, логика запускает общий пользовательский метод проверки validate().
ПРИМЕЧАНИЕ: to_internal_value() часто этот метод переопределяется, когда вам нужно настроить преобразование ваших данных в Python или если вам нужно реализовать более сложную проверку. Однако будьте осторожны; это чувствительный метод!
Поскольку обработчик передает raise_exception=True в is_valid(), он вызовет ValidationError исключение (400), если проверка завершится неудачей.
Как вы, вероятно, знаете, is_valid() достаточно умен, чтобы запустить проверку всех полей перед созданием исключения, чтобы пользователь мог получать несколько сообщений об ошибках одновременно.
Шаг 8: Выполнено действие запроса
Метод обработчика вызывает perform_create(), perform_update() или perform_destroy() в зависимости от типа метода запроса. Именно здесь происходит запрос к базе данных.
Этот шаг выполняется только в соответствующих обработчиках.
Шаг 9: Сгенерированный ответ
Наконец, метод обработчика создает экземпляр DRF Response.
Все обработчики, за исключением destroy(), возвращают представление объекта (ов) модели. Это означает, что объект (ы) необходимо сериализовать, преобразовать в JSON или другие форматы.
Для этого обработчик обращается к .data свойству сериализатора. В свою очередь, он вызывает to_representation() метод, который преобразует объекты модели в коллекцию Python (т.е. dict или list).
Позже в цикле эта коллекция Python будет передана классам рендеринга, которые сериализуют ее в требуемый формат.
ПРИМЕЧАНИЕ: Помните, что to_representation() этот метод часто переопределяется, если вам нужно настроить представление объектов вашей модели, например, условно скрыть некоторые поля.
Что происходит после того, как DRF возвращает ответ
Внутренне объект ответа DRF преобразует коллекцию Python в любой формат (ы), определенный renderer_classes в представлении или DEFAULT_RENDERER_CLASSES определенный в настройках. В большинстве случаев это JSON.
Затем ответ передается вверх по потоку и проходит те же шаги, что и в начале, но в обратном порядке. То есть:
- Django передает ответ через промежуточное программное обеспечение, затем возвращается на сервер приложений
- Сервер приложений (например, Gunicorn) передает его веб-серверу (например, Apache или Nginx)
- Веб-сервер отправляет ответ обратно пользователю
Заключение
Как вы можете видеть, цикл запрос-ответ в Django REST Framework не является чрезмерно сложным. У него есть несколько точек входа, которые вы, вероятно, настроите в зависимости от своих потребностей, но остальные могут остаться неизменными. Это признак отличной архитектуры!