Базовый класс APIView для представлений

Курс по Django: https://stepik.org/a/183363

На предыдущем занятии мы с вами создали простой проект на Django, установили Django REST Framework и реализовали тестовый пример для API. Для этого нам нужно было сделать три шага:

  • создать представление WomenAPIView;
  • создать сериализатор WomenSerializer;
  • прописать маршрут path('api/v1/womenlist/', WomenAPIView.as_view())

Причем, обратите внимание, как записывается URL-адрес для API-запросов. Сначала префикс api, затем, версия API и только потом некоторое название womenlist, отражающее суть запроса. Рекомендуется придерживаться такого правила.

Наверное, сейчас, глядя на первые два компонента (WomenAPIView и WomenSerializer) возникает некоторое непонимание их назначения и порядка работы? Поэтому данное занятие я решил посвятить детальному объяснению работы представлений в Django REST Framework. Для этого мы пока оставим сериализатор в стороне и наше приложение будет состоять из двух компонент: представления и маршрутизации.

Первым шагом (в файле women/views.py) мы определим класс WomenAPIView на основе базового APIView. Это класс, на основе которого создаются все другие классы представлений в DRF и он содержит лишь некоторый базовый функционал. Полный список классов представлений можно посмотреть на странице официальной документации:

https://www.django-rest-framework.org/api-guide/generic-views/

Импортируем класс APIView вместе с классом Response для формирования ответа в виде JSON:

from rest_framework.response import Response
from rest_framework.views import APIView

А, затем, переопределим WomenAPIView, например, так:

class WomenAPIView(APIView):
    def get(self, request):
        return Response({'title': 'Angelina Jolie'})

Мы сделали простейшую реализацию API-запроса без какого-либо сериализатора. Здесь описан один метод get(), который отвечает за GET-запрос и возвращает фиксированные данные в виде JSON-строки.

Давайте посмотрим, как это будет работать. Запустим тестовый веб-сервер:

python manage.py runserver

и отправим GET-запрос с помощью браузера:

http://127.0.0.1:8000/api/v1/womenlist/

видим ответ в виде JSON-строки.

А что будет, если по этому же адресу будет отправлен POST-запрос? Здесь сразу возникает вопрос, как это сделать, так как браузер через адресную строку формирует GET, а не POST-запросы. На мой взгляд удобнее всего воспользоваться программой, которая позволяет посылать различные запросы по указанным URL-адресам. Таких программ множество. Я воспользуюсь довольно распространенным приложением Postman, которое можно скачать по адресу:

https://www.postman.com/downloads/

После запуска мы увидим окно, где можно указать URL-адрес назначения и тип запроса:

При POST-запросе нам возвращается JSON-строка с сообщением «Метод POST не разрешен». Этот ответ был автоматически сгенерирован базовым классом APIView, который берет на себя обработку типовых ошибок при запросах, что очень удобно.

Давайте добавим в класс метод post, например, так:

class WomenAPIView(APIView):
    def get(self, request):
        return Response({'title': 'Angelina Jolie'})
 
    def post(self, request):
        return Response({'title': 'Jennifer Shrader Lawrence'})

И снова выполним POST-запрос. Теперь видим JSON-строку:

{"title": "Jennifer Shrader Lawrence"}

Конечно, все это тестовые примеры и реальный API-запрос, как правило, ожидает получения данных из таблиц БД или, какого-либо другого хранилища. В рамках нашего класса WomenAPIView это можно было бы сделать, следующим образом (для GET-запроса):

    def get(self, request):
        lst = Women.objects.all().values()
        return Response({'posts': list(lst)})

Мы здесь выбираем все записи из таблицы women, преобразуем их к списку и возвращаем в виде JSON-строки. Если теперь выполнить GET-запрос через Postman, то получим всю информацию из БД.

Аналогично поправим и POST-запрос. Как вы знаете, он используется для добавления новой информации. В нашем случае – это будет создание новой записи в таблице women:

    def post(self, request):
        post_new = Women.objects.create(
            title=request.data['title'],
            content=request.data['content'],
            cat_id=request.data['cat_id']
        )
 
        return Response({'post': model_to_dict(post_new)})

Здесь возвращается JSON-строка с содержимым добавленной записи. Для этого мы должны объект post_new преобразовать в словарь, например, с помощью функции model_to_dict фреймворка Django:

from django.forms.models import model_to_dict

Перейдем в программу Postman. Выберем POST-запрос, во вкладке «Body» отметим пункт «raw», укажем, что это будет JSON-формат и пропишем следующие данные для отправки на сервер:

{
    "title": "Sergei",
    "content": "Sergei Balakirev",
    "cat_id": 2
}

Обратите внимание, что данные для нашего API должны отправляться в JSON-формате, а не как простые текстовые данные, то есть, заголовок должен иметь обозначение «application/json». Все взаимодействие и при получении данных и при отправке происходит по умолчанию в JSON-формате. Про это не нужно забывать.

Нажимаем отправить «Send» и видим, что запись была успешно добавлена в БД. Разумеется, если указать неполные данные, например, убрать заголовок, то у нас возникнут ошибки при выполнении метода post(). То есть, здесь данные не проверяются на валидность модели. Но нам эту проверку делать нет смысла, т.к. при работе с моделями применяются соответствующие классы представлений, где эта обработка уже встроена. Здесь я лишь показываю принцип работы представлений в Django REST Framework.

Помимо методов get() и post() можно прописывать и другие для обработки соответствующих типов запросов:

  • get() – вызывается при GET-запросах;
  • post() – вызывается при POST-запросах;
  • put() – вызывается при PUT-запросах;
  • patch() – вызывается при PATCH-запросах;
  • delete() – вызывается при DELETE-запросах.

У вас здесь может возникнуть вопрос, а как одно и то же представление для одного и того же URL-адреса «понимает», какой из методов следует вызывать, когда приходит какой-либо запрос от клиента? В действительности, все просто. Когда клиент формирует запрос, то в его заголовке прописывается тип этого запроса: GET, POST, DELETE, PUT и т.п. Затем, алгоритм, заложенный в представления DRF автоматически вызывает метод, связанный с поступившим запросом. Если нужный метод не находится, то клиенту возвращается JSON-строка с сообщением, что метод не разрешен.

Итак, на этом занятии мы с вами создали простейшее представление на основе базового класса APIView и сами, вручную создавали сериализованные данные в виде JSON-ответа. Целью данного занятия было показать, как на низком уровне без сериализаторов можно самостоятельно формировать данные и реализовывать простейшие API с одним классом представления. На следующем занятии мы сделаем очередной шаг в понимании работы Django REST Framework и также детально опишем и добавим простой сериализатор в наше приложение.

Курс по Django: https://stepik.org/a/183363