Для понимания, что такое ViewSet, представьте, что вы строите дом. Если APIView — это кирпич, из которого вы складываете стены, то ViewSet — это уже готовый модуль стены с окнами и дверями, который можно сразу вставить в дом. То есть ViewSet позволяет объединять в единый класс всю CRUD-логику для работы с моделью.
ViewSet в DRF — это класс, упрощающий создание нескольких обработчиков HTTP-запросов (GET, POST, PUT, DELETE) в одном месте. Вместо написания отдельных методов для каждого действия, вы можете связать их логически и реализовать в одном классе.
Но не думайте, что ViewSet сделает за вас всю работу! Вам всё равно придётся писать бизнес-логику. Просто теперь всё будет более структурировано.
Преимущества использования ViewSet
Единая точка реализации CRUD-операций: ViewSet охватывает всю логику для операций создания, чтения, обновления и удаления записей (CRUD). Это упрощает и ускоряет разработку.
Упрощённая маршрутизация: вместо того чтобы вручную прописывать маршруты для каждого метода, мы можем использовать
Router, который автоматически создаёт маршруты на основе ViewSet.Повышение читаемости и масштабируемости кода: всё поведение API для одной сущности сосредоточено в одном месте — в классе ViewSet, что делает код более аккуратным и удобным для сопровождения.
Структура ViewSet
В отличие от APIView, где мы писали методы вроде get(), post() и так далее, ViewSet уже поставляется с готовым набором методов:
list(self, request, *args, **kwargs)— обработка запросов для получения списка объектов (аналогGETна уровне коллекции, например,/api/books/).retrieve(self, request, *args, **kwargs)— обработка запросов для получения одного объекта (аналогGETна уровне объекта, например,/api/books/1/).create(self, request, *args, **kwargs)— обработка запросов для создания нового объекта (аналогPOST).update(self, request, *args, **kwargs)— обработка запросов для полного обновления объекта (аналогPUT).partial_update(self, request, *args, **kwargs)— частичное обновление объекта (аналогPATCH).destroy(self, request, *args, **kwargs)— обработка запросов для удаления объекта (аналогDELETE).
Эти методы уже предопределены в DRF, и вам остаётся только использовать их с умом.
Как ViewSet объединяет CRUD-логику?
Когда вы создаёте ViewSet, вы описываете все операции для работы с объектами одной модели в одном классе. Такой подход делает код компактным и легко тестируемым. Например:
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class BookViewSet(ViewSet):
def list(self, request):
# Логика для обработки запроса GET /api/books
return Response({"message": "Список всех книг"})
def retrieve(self, request, pk=None):
# Логика для обработки запроса GET /api/books/{id}
return Response({"message": f"Получение книги с id {pk}"})
def create(self, request):
# Логика для обработки запроса POST /api/books
return Response({"message": "Создание новой книги"})
def update(self, request, pk=None):
# Логика для обработки запроса PUT /api/books/{id}
return Response({"message": f"Обновление книги с id {pk}"})
def destroy(self, request, pk=None):
# Логика для обработки запроса DELETE /api/books/{id}
return Response({"message": f"Удаление книги с id {pk}"})
Каждый метод отвечает за обработку своего запроса. Это либо операция на уровне коллекции (list, create), либо на уровне отдельного объекта (retrieve, update, destroy).
Пример использования ViewSet
Давайте посмотрим, как ViewSet применяется на практике. Возьмём за основу нашу модель Book.
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
published_date = models.DateField()
isbn = models.CharField(max_length=13, unique=True)
def __str__(self):
return self.title
Создадим соответствующий сериализатор для модели:
# serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
Теперь создадим ViewSet для Book:
# views.py
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer
class BookViewSet(ViewSet):
def list(self, request):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
book = Book.objects.get(pk=pk)
serializer = BookSerializer(book)
return Response(serializer.data)
def create(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
def update(self, request, pk=None):
book = Book.objects.get(pk=pk)
serializer = BookSerializer(book, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=400)
def destroy(self, request, pk=None):
book = Book.objects.get(pk=pk)
book.delete()
return Response(status=204)
Как подключить ViewSet к маршрутам?
Маршруты для ViewSet проще всего настроить через Router. Благодаря этому DRF сам позаботится о создании маршрутов для list, retrieve и других методов.
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = [
path('', include(router.urls)),
]
Теперь эти маршруты работают «из коробки»:
GET /books→ список книг.GET /books/{id}→ информация о книге.POST /books→ создание новой книги.PUT /books/{id}→ обновление книги.DELETE /books/{id}→ удаление книги.
На что обратить внимание?
- Обработка исключений. Всегда оборачивайте действия, вроде
Book.objects.get(pk=pk), вtry/except. В противном случае, если объект не найден, ваш сервер будет возвращать неинформативные 500-ки. - Кастомизация методов. Если стандартных методов недостаточно, вы всегда можете добавить свои кастомные методы (об этом позже на лекции 199).
- Производительность. Не забывайте про оптимизацию запросов и использование
select_related()илиprefetch_related()при работе со связанными моделями.
ViewSets — это мощный инструмент, позволяющий быстро создать API, сохраняя при этом код чистым и удобным. В следующих лекциях мы разберём более сложные виды ViewSet, такие как ModelViewSet, и научимся настраивать маршруты через Router для максимальной гибкости.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ