GenericViewSet — це гібрид між класичними ViewSet і Generic Views. Він надає потужний, але гнучкий базовий клас для створення ViewSet, комбінуючи можливості generic-based views (наприклад, такі класи як ListAPIView, RetrieveAPIView) з механікою ViewSet. Це дозволяє нам більш точно керувати доступними діями (CRUD-операціями), обираючи лише ті, що нам потрібні.
Якщо ModelViewSet автоматизує стандартні дії (CRUD) "з коробки", то GenericViewSet вимагає, щоб ви явно вказали, які дії ви збираєтесь використовувати. Він не бере на себе всю магію, що робить його більш кастомізованим.
Чому варто використовувати GenericViewSet?
- Гнучкість: ви контролюєте, які дії доступні у вашому API.
- Уникнення зайвого коду: використовуєте лише ті методи, які потрібні.
- Розширюваність: легко додавати додаткові кастомні методи.
Як це працює?
GenericViewSet працює у поєднанні з міксинами (mixins) — це спеціальні класи, які надають реалізацію типових операцій CRUD. Ось основні міксини, які ви можете використовувати з GenericViewSet:
mixins.ListModelMixin: для повернення списку об'єктів.mixins.CreateModelMixin: для створення нового об'єкта.mixins.RetrieveModelMixin: для отримання одного об'єкта.mixins.UpdateModelMixin: для оновлення об'єкта.mixins.DestroyModelMixin: для видалення об'єкта.
Ви можете комбінувати ці міксини так, як вам потрібно, і таким чином керувати функціоналом ViewSet.
Приклад використання GenericViewSet
Давайте реалізуємо простий приклад. У нас є модель Book з атрибутами title і author. Ми хочемо налаштувати API для роботи з цією моделлю, але, скажімо, хочемо обмежитися тільки функціоналом читання (List і Retrieve).
1.Визначення моделі Book
Створимо модель книги, якщо ви ще не зробили це в проєкті:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
def __str__(self):
return self.title
Не забудьте виконати міграції:
python manage.py makemigrations
python manage.py migrate
Створення серіалізатора
Тепер налаштуємо серіалізатор для нашої моделі:
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
Налаштування GenericViewSet
Тепер створимо ViewSet з використанням
GenericViewSetі необхідних міксинів:
from rest_framework import viewsets, mixins
from .models import Book
from .serializers import BookSerializer
class BookViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
"""
Цей ViewSet дозволяє:
- Отримати список усіх книг (ListModelMixin)
- Отримати детальну інформацію про одну книгу (RetrieveModelMixin)
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
Зверніть увагу, що ми використовуємо два міксини: ListModelMixin і RetrieveModelMixin. Це означає, що наш ViewSet буде підтримувати тільки дві дії: list (GET /books/) і retrieve (GET /books/<id>/).</id>
- Налаштування маршрутів
Додамо маршрути для нашого ViewSet з використанням DefaultRouter:
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = router.urls
- Тестування API
Запустіть сервер і протестуйте ендпоінти:
- Отримати список книг:
GET /books/ - Отримати конкретну книгу:
GET /books/<id>/
Ви можете використовувати Postman, cURL або вбудований браузер API DRF для тестування.
Кастомізація GenericViewSet
Іноді вам може знадобитися додати додаткову логіку в ваш ViewSet. Наприклад, ви хочете мати кастомний метод для пошуку книг за автором. Для цього ми можемо визначити власну дію всередині ViewSet.
from rest_framework.decorators import action
from rest_framework.response import Response
class BookViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
@action(detail=False, methods=['get'])
def by_author(self, request):
"""
Кастомна дія для пошуку книг за автором.
Наприклад: /books/by_author/?author=Толстой
"""
author = request.query_params.get('author', None)
if author is not None:
books = self.queryset.filter(author__icontains=author)
serializer = self.get_serializer(books, many=True)
return Response(serializer.data)
return Response({"error": "Параметр author є обов'язковим."}, status=400)
Тепер у вашому API з'явиться новий маршрут:
- Пошук книг за автором:
GET /books/by_author/?author=<ім'я автора>
Деталі кастомної дії:
- Метод
@actionдозволяє створити додаткову дію. detail=Falseвказує, що ця дія працює зі списком (не з окремим об'єктом).- Ви можете додавати свої параметри запиту через
request.query_params.
Коли використовувати GenericViewSet?
GenericViewSet ідеально підходить для випадків, коли вам потрібно:
- Обмежити функціонал дій (наприклад, тільки
listіretrieve). - Додати кастомні методи без зайвого коду.
- Розділити логіку і зробити API більш читабельним.
Наприклад, якщо у вас складний додаток із багатьма моделями, і не всі з них потребують повної реалізації CRUD, використання GenericViewSet разом із вибраними міксинами дозволить вам уникнути зайвого коду і зробити API більш керованим.
Типові помилки при роботі з GenericViewSet
Відсутність міксинів.
GenericViewSetсам по собі не надає методів, таких якlist,retrieve,create. Якщо ти забудеш додати міксини, отримаєш помилку 405 (Method Not Allowed).Неправильні налаштування маршрутів. Використання неправильного
basenameабо пропуск реєстрації ViewSet у роутері може призвести до того, що твій API не буде доступним.Неявне повернення даних. Якщо ти забув використати
Responseу кастомних методах, твій API може поводитися дивно.Необроблені винятки. Додаючи кастомні методи, переконайся, що ти коректно обробляєш помилки (наприклад, відсутній параметр або некоректний формат даних).
Реальне застосування
Робота з GenericViewSet знадобиться в проєктах, де потрібно:
- Грамотний розподіл відповідальності між методами API.
- Мінімалізм для "допоміжних" моделей (наприклад, моделі налаштувань або довідники).
- Гнучкість у додаванні та кастомізації методів.
У реальному житті розширення GenericViewSet може бути корисним не лише для API, але й для створення допоміжних методів у великих проєктах.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ