Сегодня мы углубимся в различия между классическими представлениями (View или APIView) и их более мощным и удобным "коллегой" — ViewSet. Мы сравним их функционал, обсудим, когда лучше использовать тот или иной подход, и, конечно, рассмотрим примеры.
View (или APIView): базовый функционал
Начнём с классических представлений. Это как фундаментальный инструмент для создания API в DRF: он простой, удобный, но требует от разработчика больше кода, если вам нужно реализовать несколько действий для одного и того же ресурса.
APIView — это DRF-шный аналог стандартного Django-класса View. Он даёт нам доступ к:
- Методу
requestдля обработки HTTP-запросов (GET, POST, PUT, DELETE и др.). - Встроенной обработке сериализации данных.
- Простому способу возврата HTTP-ответов.
Пример базового APIView:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class ExampleView(APIView):
def get(self, request):
# Логика обработки GET запроса
data = {"message": "Привет, мир!"}
return Response(data, status=status.HTTP_200_OK)
def post(self, request):
# Логика обработки POST запроса
data = {"message": "Вы отправили данные:", "data": request.data}
return Response(data, status=status.HTTP_201_CREATED)
Пример выше прост и понятен, но если нам нужно добавить больше операций (например, PUT, DELETE), это потребует дополнительного кода и тестирования.
ViewSet: Мощь и удобство
Теперь перейдём к ViewSet. Это как универсальный швейцарский нож для создания API: он позволяет упростить написание кода, объединяя все CRUD-операции в одном месте.
1. Что такое ViewSet?
ViewSet — это абстракция, которая объединяет все CRUD-операции в одном классе. Вместо того чтобы писать отдельные методы для каждого HTTP-метода (get, post, put и т.д.), вы описываете свои действия в одном месте, а DRF сам создаёт маршруты для вашего API.
Пример базового ViewSet:
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class ExampleViewSet(ViewSet):
def list(self, request):
# GET запрос на получение списка объектов
data = {"message": "Список данных"}
return Response(data)
def retrieve(self, request, pk=None):
# GET запрос для получения одного объекта по его ID
data = {"message": f"Данные для объекта {pk}"}
return Response(data)
def create(self, request):
# POST запрос для создания нового объекта
data = {"message": "Создан новый объект", "data": request.data}
return Response(data)
Здесь DRF автоматически создаёт маршруты для действий list, retrieve и create. Например:
- list →
GET /example/ - retrieve →
GET /example/<id>/ - create →
POST /example/
Меньше кода = меньше ошибок и головной боли.
Сравнение View и ViewSet
Теперь давайте разберём основные различия между APIView и ViewSet.
- Объём кода
Представим, что мы хотим реализовать базовые CRUD-операции (создание, чтение, обновление, удаление) для ресурса "Книги". Пример через APIView:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class BookListView(APIView):
def get(self, request):
# Возвращаем список книг
data = {"books": ["Книга 1", "Книга 2"]}
return Response(data)
def post(self, request):
# Создаём новую книгу
data = {"message": "Книга создана"}
return Response(data, status=status.HTTP_201_CREATED)
class BookDetailView(APIView):
def get(self, request, pk):
# Получаем данные книги по ID
data = {"book": f"Данные для книги {pk}"}
return Response(data)
def put(self, request, pk):
# Обновляем данные книги
data = {"message": f"Книга {pk} обновлена"}
return Response(data)
def delete(self, request, pk):
# Удаляем книгу
data = {"message": f"Книга {pk} удалена"}
return Response(data, status=status.HTTP_204_NO_CONTENT)
Мы разделили логику на два класса: BookListView и BookDetailView. Да, всё работает, но получилось многословно. А теперь, тот же функционал на ViewSet:
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class BookViewSet(ViewSet):
def list(self, request):
# GET /books/
data = {"books": ["Книга 1", "Книга 2"]}
return Response(data)
def create(self, request):
# POST /books/
data = {"message": "Книга создана"}
return Response(data)
def retrieve(self, request, pk=None):
# GET /books/<pk>/
data = {"book": f"Данные для книги {pk}"}
return Response(data)
def update(self, request, pk=None):
# PUT /books/<pk>/
data = {"message": f"Книга {pk} обновлена"}
return Response(data)
def destroy(self, request, pk=None):
# DELETE /books/<pk>/
data = {"message": f"Книга {pk} удалена"}
return Response(data)
Код короче, а маршруты настраиваются автоматически. Например, вместо ручного прописывания URL мы можем использовать маршрутизаторы Router (но это будет в следующих лекциях).
- Гибкость
APIView обеспечивает больший контроль над реализацией. Она лучше подходит, если логика отличается от стандартных CRUD-операций. Например, если вы хотите реализовать какие-то бизнес-операции (сложные вычисления, нестандартные запросы).
ViewSet, в свою очередь, упрощает создание API для стандартных CRUD-операций, где всё, что нужно, уже под капотом.
- Когда использовать?
Используйте
APIViewилиView, если:- У вас специфичные требования к реализации эндпоинта.
- Вы хотите гибко управлять логикой обработки запросов.
Используйте
ViewSet, если:- Вам нужно быстро создать API для стандартных CRUD-операций.
- Вы работаете с сериализаторами и стандартными методами работы с моделями.
Примеры выбора между View и ViewSet
Сценарий 1: простой просмотр данных. Если API нужно только для простого просмотра данных (создание, чтение, обновление, удаление), ViewSet — ваш выбор. Он уменьшает количество кода и автоматизирует маршрутизацию.
Сценарий 2: Кастомная логика. Если у вас сложные операции, которые не вписываются в CRUD, например, обработка больших файлов, отдельные классы на основе APIView подойдут лучше. Например:
class FileUploadView(APIView):
def post(self, request):
# Логика загрузки файла
file = request.FILES['file']
process_file(file) # Допустим, вы обрабатываете файл
return Response({"message": "Файл обработан"})
TL;DR: Кто лучше?
Если вы строите CRUD API для ресурса — ViewSet сократит время и избавит от лишнего шаблонного кода. Если вам нужно больше гибкости или кастомной логики — APIView ваш лучший друг.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ