В этой части нашей серии полных руководств объясняется, что такое GenericAPIViews в Django Rest Framework и как их использовать.
В этой части нашей серии руководств объясняется, что такое GenericAPIViews в Django Rest Framework и как их использовать. Это третья часть серии. Вот краткое изложение всей серии:
- Во второй части рассказывается о Serializer и ModelSerializer классах, а также объясняется их назначение и использование.
- В третьей части рассматриваются GenericAPIView и его родственные классы, предназначенные для работы с моделями баз данных. Это классы, которые вы собираетесь использовать чаще всего.
Предварительные условия и допущения
- У вас есть базовое представление о REST API.
- У вас есть базовые знания о Django Framework.
- В вашей системе установлен Python 3.6+.
- Установлены все необходимые зависимости
- Имеется созданный django-проект с хотя бы одним приложением,
- Выполнены все миграции
Что такое GenericAPIView в DRF?
Мы уже рассматривали основополагающий APIView класс в первой части и к настоящему моменту знаем, что, хотя он предоставляет базу для конечных точек API, в нем отсутствуют обработчики запросов и инструменты для работы с моделями баз данных.
На самом деле приложение вряд ли может существовать без базы данных. И хотя вы можете заставить APIVIew класс взаимодействовать с моделями баз данных, вам придется вручную расширить его несколькими методами, чтобы выполнять следующие действия:
- Наборы запросов модели выборки
- Найти один экземпляр модели или вернуть ошибку 404, если модель не существует
- Взаимодействие с сериализатором для обработки и сохранения данных запроса
- Разбиение наборов запросов на страницы
Скорее всего, вы в конечном итоге создадите подкласс APIView, содержащий методы для обработки вышеизложенного, и будете повторно использовать его во всех своих представлениях. Что ж, GenericAPIView это именно такой класс!
Как использовать GenericAPIViews?
Есть два основных свойства, которые вам всегда нужно указывать при наследовании вашего представления от GenericAPIView. Это queryset и serializer_class. Давайте рассмотрим краткий пример того, как их установить:
в "заглушках" модели и сериализатора ничего пока нет...
ПРИМЕЧАНИЕ: На самом деле мы редко наследуем напрямую от GenericAPIView, потому что в нем отсутствуют какие-либо обработчики запросов, такие как get(), post() и т.д. Существует ряд подклассов GenericAPIView, которые имеют эти обработчики. Мы рассмотрим эти подклассы далее в статье.
Как упоминалось ранее, GenericAPIView представлены инструменты для поиска отдельного объекта из заданного набора запросов. По умолчанию для этого используется pk поле.
Чтобы использовать эту функциональность, мы должны зафиксировать pk в URL конечной точки. Наиболее распространенным подходом для этого является регистрация нашего представления следующим образом:
Но иногда нам может потребоваться выполнить поиск экземпляра по полю, отличному от pk. Для этого мы должны установить lookup_field свойство в нашем классе view. Обратите внимание, что нам также потребуется изменить имя параметра captured value при регистрации маршрута:
Если по какой-либо причине мы хотим, чтобы у параметра захваченного значения было другое имя, чем у нашего, lookup_field мы можем установить lookup_url_kwarg параметр в нашем представлении:
Наконец, стоит упомянуть, что мы можем указывать политики API в нашем представлении точно так же, как мы делали с обычными APIViews:
Как работает GenericAPIView под капотом?
Под капотом GenericAPIView представлен ряд методов, реализующих функциональность, о которой мы упоминали в начале этой статьи.
Хотя большинство из них не предназначалось для непосредственного использования, стоит знать хотя бы некоторые из них – особенно те, которые вы, возможно, захотите переписать при работе со сложной конечной точкой.
ПРИМЕЧАНИЕ: Методы GenericAPIView, представленные в этом разделе, несколько упрощены для примера.
Начиная с обработки набора запросов, GenericAPIView имеет метод с именем get_queryset(), который делает именно то, что следует из названия. Вот представление логики этого метода:
Вам может быть интересно – какова цель использования метода, когда у нас уже есть queryset свойство? Это позволяет нам легко переписать его, если нам нужна какая-то пользовательская функциональность. Например:
Идем дальше. У GenericAPIView класса также есть метод для создания экземпляра класса сериализатора с именем get_serializer(). Вот как выглядит его реализация:
Обратите внимание, как он вызывает два других метода: get_serializer_class() и get_serializer_context().
Идея, лежащая в основе, get_serializer_class() та же, что и в get_queryset() методе, – обеспечить простую настройку. Например, вы могли бы сделать что-то вроде этого:
Второй метод - get_serializer_context() – передает экземпляру сериализатора несколько свойств:
Если вы помните из приведенного выше фрагмента, get_serializer() метод передает этот контекстный объект конструктору serializer через kwargs под context ключом. Это означает, что вы можете получить доступ к контексту в вашем коде сериализатора следующим образом:
И последнее, но не менее важное: у GenericAPIView класса есть get_object() метод, который выполняет поиск по одному объекту в наборе запросов. Вот обзор его реализации:
В завершение этого раздела стоит сказать, что, несмотря на то, что GenericAPIView поставляется со всеми этими удобными функциями, мы редко наследуем наши представления непосредственно от него. Давайте посмотрим, почему.
Почему мы не используем GenericAPIView напрямую?
На самом деле мы почти никогда не создаем подклассы наших представлений непосредственно из, GenericAPIView потому что, как и APIView, это промежуточный класс, который излагает базовую логику для работы с моделями баз данных, но не содержит обработчиков запросов.
Однако существует несколько подклассов, GenericAPIView которые содержат обработчики запросов, и именно эти классы вы будете использовать большую часть времени. Вот их полный список:
- ListAPIView
- CreateAPIView
- RetrieveAPIView
- UpdateAPIView
- DestroyAPIView
- ListCreateAPIView
- RetrieveUpdateAPIView
- RetrieveDestroyAPIView
- RetrieveUpdateDestroyAPIView
Это довольно много, не так ли? Названия этих подклассов указывают на их назначение, но чтобы было предельно ясно, как они работают, давайте обновим несколько сведений о REST API и посмотрим, как с ними соотносится Django REST Framework.
Почему так много подклассов GenericAPIView?
Чтобы понять, почему существует так много подклассовGenericAPIView, мы должны быстро обновить пару вещей о REST API:
Во-первых, REST API обычно делятся на две группы по их области действия, которая может быть одной из следующих:
- Список объектов
- Единый объект
В Django Rest Framework эти области называются списком и подробностями.
Во-вторых, REST API используют HTTP-методы для идентификации операций. Наиболее часто используемые методы запроса являются:
- GET - для извлечения списка объектов или отдельного объекта
- POST - для создания новых объектов
- PUT и PATCH - для редактирования существующих объектов
- DELETE - для удаления объектов
Таким образом, набор конечных точек, который позволяет выполнять все операции CRUD над моделью базы данных, может выглядеть так, как показано в таблице ниже:
Как вы, вероятно, поняли, подклассы GenericAPIView позволяют нам создавать каждую из этих конечных точек. Давайте теперь углубимся в то, как они это делают.
Как работают подклассы GenericAPIView
Основная цель GenericAPIView подклассов - предоставить реализации обработчиков запросов. Однако стоит упомянуть, что DRF хранит фактические реализации обработчиков запросов в виде набора “миксинов”.
ПРИМЕЧАНИЕ: На случай, если это покажется вам новым, миксины в Python - это крошечные классы, содержащие фрагменты кода, которые можно повторно использовать. Они могут быть "подмешаны" при объявлении класса благодаря множественному наследованию в Python.
Как вы скоро увидите, подклассы GenericAPIView используют миксины DRF. Вот их полный список:
- ListModelMixin
- CreateModelMixin
- RetrieveModelMixin
- UpdateModelMixin
- DestroyModelMixin
Названия не требуют пояснений, но мы углубимся в каждую реализацию в следующем разделе.
Чтобы лучше понять иерархию представлений Django REST Framework, уделите минутку изучению этой диаграммы (я люблю визуальные материалы).
Теперь давайте погрузимся в код подклассов GenericAPIView и посмотрим, как они используют эти миксины.
Как работает ListAPIView?
Как следует из названия, ListAPIView класс позволяет нам отображать список объектов модели. Это представление в виде списка, которое предоставляет реализацию для GET /wizards конечной точки из приведенной выше таблицы:
Логика довольно проста: мы извлекаем набор запросов, передаем его сериализатору, а затем возвращаем сериализованные данные.
ПРИМЕЧАНИЕ: На самом деле list() метод также разбивает набор запросов на страницы, если pagination_class он указан глобально или локально в представлении.
Как работает CreateAPIView?
Следующее - это другое представление области списка – CreateAPIView класс. Как следует из названия, оно позволяет нам создавать новые записи базы данных и предоставляет реализацию для POST /wizards конечной точки из нашей таблицы:
Здесь стоит обратить внимание на пару вещей:
Во-первых, вызов save() метода serializer, который вы, возможно, помните из второй части серии, заключен в его собственный метод с именем perform_create(). Это сделано для того, чтобы мы могли легко настроить это поведение, переписав метод, если нам когда-нибудь понадобится.
Во-вторых, Response возвращается со 201 - Created статусом. Это соответствует стандартам спецификации REST API.
Как работает RetrieveAPIView?
RetrieveAPIViewКласс представляет собой представление области детализации. Это означает, что он использует get_object() метод, упомянутый ранее. Как следует из названия, RetrieveAPIView просто показывает запрошенный объект. Он предоставляет реализацию GET /wizards/:id конечной точки из нашей таблицы:
Логика довольно проста: мы извлекаем набор запросов, а затем ищем требуемый экземпляр по его pk или любому другому указанному полю.
Возможно, вы помните, что если экземпляр не может быть найден, get_object() метод выдаст 404 - Not Found ошибку.
Наконец, мы сериализуем экземпляр и возвращаем данные.
Как работает UpdateAPIView?
UpdateAPIViewКласс также является представлением области детализации, которое переносит логику обновления экземпляров модели в таблицу. Он обеспечивает реализацию PUT|PATCH /wizards/:id конечной точки из приведенной выше таблицы:
Это сочетание, пожалуй, самое сложное из всех. Но если присмотреться повнимательнее, логика также довольно проста:
Мы проверяем, передан ли partial аргумент. Если да, то мы знаем, что это PATCH запрос. Однако, как с этим справиться, зависит от сериализатора. Наша задача - просто сообщить об этом.
Далее мы ищем экземпляр модели с помощью get_object(), которая выдаст 404 - Not Found ошибку, если экземпляр не может быть найден.
Затем мы создаем экземпляр сериализатора, передавая ему partial аргумент, и после этого выполняем проверку.
Еще раз, вызов save() метода сериализатора заключен в отдельный метод с именем perform_update(). Как обычно, это сделано для того, чтобы мы могли легко перезаписать этот метод, если нам когда-нибудь понадобится настроить поведение.
Как работает DestroyAPIView?
Последним представлением в нашем списке является DestroyAPIView класс. Он обеспечивает реализацию DELETE /wizards/:id конечной точки из нашей таблицы, что означает, что это также представление с детализацией:
И этот микс, пожалуй, самый простой из всех, хотя бы потому, что он даже не использует сериализатор.
Мы просто ищем объект с помощью get_object() или выдаем 404 - Not Found ошибку, если экземпляр не может быть найден.
После этого мы выполняем прямой delete() вызов экземпляра, который преобразуется в отдельный метод с именем perform_destroy(). Как и в других подобных случаях, это делается для легкой настройки поведения.
Обратите внимание, как мы возвращаем пустой ответ со 204 - No Content статусом в конце. Это соответствует стандартам спецификации REST API.
Остальные подклассы GenericAPIView
Все остальные подклассы представляют собой просто комбинацию двух или трех миксинов, описанных выше:
- ListCreateAPIView сочетает в себе ListModelMixin и CreateModelMixin
- RetrieveUpdateAPIView сочетает в себе RetrieveModelMixin и UpdateModelMixin
- RetrieveDestroyAPIView сочетает в себе RetrieveModelMixin и DestroyModelMixin
- RetrieveUpdateDestroyAPIView сочетает в себе RetrieveModelMixin, UpdateModelMixin и DestroyModelMixin
Причина, по которой DRF предоставляет их, заключается не только в экономии нашего времени и усилий, но и в том, что невозможно зарегистрировать два представления по одному и тому же маршруту. Рассмотрим этот пример:
Если вы зарегистрируете два представления, как в примере выше, и попытаетесь выполнить POST вызов/api/wizards, вы получите 405 - Method Not Allowed сообщение об ошибке. Это связано с тем, как Django регистрирует просмотры: будет зарегистрирован только первый.
Таким образом, объединение нескольких обработчиков запросов позволяет нам регистрировать их по одному и тому же маршруту:
Заключение
В этой главе серии всеобъемлющих руководств мы подробно рассмотрели GenericAPIView класс и его подклассы.
Мы узнали, что он поставляется с методами для извлечения набора запросов к базе данных и поиска конкретного экземпляра модели, а также методами для работы с сериализатором модели.
Мы также изучили иерархию представлений в Django REST Framework и узнали о классах mixin, которые предоставляют реализации обработчиков запросов, и о том, что подклассы GenericAPIView создаются с помощью этих mixins.
Эти смешанные классы также используются в ViewSet объявлении класса, которое мы собираемся изучить в следующей части серии.