View-классы API в Django

Следующий этап работы с Views в Django REST framework — встроенные view-классы. У них множество преимуществ перед view-функциями:

  • возможность применять готовый код для решения стандартных задач;
  • наследование, которое позволяет повторно использовать уже написанный код.

Встроенные классы DRF можно условно разделить на низкоуровневые и высокоуровневые. Низкоуровневые содержат лишь базовую структуру класса, его скелет; разработчик сам должен описать работу класса; их применяют для решения нестандартных задач.

Типовые задачи (скажем, CRUD) удобнее решать с помощью высокоуровневых view-классов: в них уже заготовлены все инструменты для решения стандартных задач.

Низкоуровневые view-классы в DRF

Начнём с низкоуровневого view-класса APIView из модуля rest_framework.views.

Если view-класс унаследован от класса APIView, то при получении GET-запроса в классе будет вызван метод get(), а при получении POST-запроса — метод post(). Такие методы описаны для всех типов запросов, но по умолчанию эти методы не выполняют никаких действий, их нужно описывать самостоятельно.

# Скелет есть, а кода нет. Надо самостоятельно описать необходимые методы.
class MyAPIView(APIView):
    def get(self, request):
        ...

    def post(self, request):
        ...

    def put(self, request):
        ...

    def patch(self, request):
        ...

    def delete(self, request):
        ... 

В целом этот класс работает так же, как и view-функции.

Практика: редактируем вместе

Измените проект Kittygram: вместо view-функции опишите view-класс.

Импортируйте класс APIView из rest_framework.views, создайте класс-наследник и переопределите в нём методы get() и post(). Код почти ничем не будет отличаться от того, что был во view-функции, но будет написан в объектно-ориентированном стиле.

# Обновлённый views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .models import Cat
from .serializers import CatSerializer


class APICat(APIView):
    def get(self, request):
        cats = Cat.objects.all()
        serializer = CatSerializer(cats, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = CatSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED) 
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

Чтобы всё заработало, исправьте код в urls.py, ведь синтаксис вызова view-классов отличается от синтаксиса вызова view-функций.

# urls.py
from django.urls import include, path

from cats.views import APICat

urlpatterns = [
    path('cats/', APICat.as_view()),
] 

Как и при работе со view-функциями, все операции CRUD при использовании view-классов принято разделять на 2 группы: в одном view-классе описывается создание нового объекта и запрос всех объектов (например класс APICat), а в другом классе — получение/изменение/удаление определённого объекта (например класс APICatDetail).

Внесите изменения в Kittygram, запустите проект и поработайте с запросами через Postman: API будет работать так же, как и со view-функциями.

Generic Views: высокоуровневые view-классы

Для типовых действий, например, для вывода списка объектов или для запроса объекта по id удобнее использовать высокоуровневые view-классы, «дженерики» (англ. Generic Views): в них уже реализованы все механизмы, необходимые для решения задачи.

Некоторые из Generic Views выполняют строго определённую задачу (например, обрабатывают только один тип запросов), другие — более универсальны и могут «переключаться» на разные задачи в зависимости от HTTP-метода, которым был отправлен запрос.

В дженериках задают всего два поля: queryset (набор записей, который будет обрабатываться в классе) и serializer_class (сериализатор, который будет преобразовывать объекты в формат JSON). В DRF все Generic Views объединены в модуле rest_framework.generics. Полный список можно посмотреть в документации DRF.

Рефакторинг кода: пишем Kittygram на Generic Views

Операции, описанные во view-классе проекта Kittygram, типичны для любого API. Есть смысл переписать код, заменив низкоуровневый view-класс на дженерики.

Для работы возьмём два класса и на них реализуем все шесть операций классического API:

  • комбинированный класс ListCreateAPIView: он возвращает всю коллекцию объектов (например, всех котиков) или может создать новую запись в БД;
  • комбинированный класс RetrieveUpdateDestroyAPIView: его работа — возвращать, обновлять или удалять объекты модели по одному.

Это практическая работа: вносите изменения, описанные в этом уроке, в проект Kittygram, развёрнутый на вашем компьютере.

Импортируйте в код всё необходимое: generics из rest_framework, модель и сериализатор; затем опишите дженерики:

# Обновлённый views.py
from rest_framework import generics

from .models import Cat
from .serializers import CatSerializer


class CatList(generics.ListCreateAPIView):
    queryset = Cat.objects.all()
    serializer_class = CatSerializer


class CatDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Cat.objects.all()
    serializer_class = CatSerializer 

Измените вызов view-класса в urls.py:

# Обновлённый urls.py
from django.urls import include, path

from cats.views import CatList, CatDetail

urlpatterns = [
    path('cats/', CatList.as_view()),
    path('cats/<int:pk>/', CatDetail.as_view()),
] 

Теперь проект Kittygram поддерживает весь API CRUD для модели Cat (а не только получение списка всех котиков и добавление нового котика). При минимальном количестве изменений в коде мы сделали API для модели Cat в Kittygram! Да и кода стало меньше; а где меньше кода, там меньше ошибок.

В коде явным образом не описана обработка разных типов запросов: всё происходит «под капотом» view-классов. Внесите изменения в свой локальный проект Kittygram и отправьте запросы разного типа через Postman: убедитесь в работоспособности проекта.

Специализированные Generic Views

В коде Kittygram view-классы унаследованы от комбинированных дженериков: они выполняют все операции CRUD. Для решения нашей задачи именно комбинированные дженерики подходят лучше всего. Но в некоторых случаях применение комбинированных view-классов будет избыточным или даже опасным.

Например, при создании API «только для чтения» лучше подключить специализированный ListAPIView, который выполняет ровно одно действие: выводит список объектов в ответ на GET-запрос. Это лучше и с точки зрения безопасности, и с точки зрения отсутствия избыточного кода.

Есть ещё несколько специализированных view-классов DRF:

  • RetrieveAPIView — возвращает один объект (обрабатывает только GET-запросы);
  • CreateAPIView — создаёт новый объект (обрабатывает только POST-запросы);
  • UpdateAPIView — изменяет объект (обрабатывает только PUT- и PATCH-запросы);
  • DestroyAPIView — удаляет объект (обрабатывает только DELETE-запросы).

Эти классы описываются в коде точно так же, как и комбинированные view-классы модуля rest_framework.generics.

Что в итоге

Работа с view-классами в DRF отличается от работы с привычными view-классами в Django только сериализацией и отсутствием HTML-шаблонов.

Удобная шпаргалка по классам DRF. Добавьте эту страницу в закладки и заглядывайте туда регулярно: с каждым разом этот справочник будет становиться всё понятнее и полезнее.





Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: