Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
ViewSet в Django REST Framework — комбинированный контроллер
ViewSet — это класс, который объединяет логику нескольких CRUD операций для одного ресурса. Вместо создания отдельных View классов, пишем один ViewSet.
Из чего состоит ViewSet
1. Основные компоненты (действия)
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# Встроенные действия (CRUD):
# - list() → GET /users/ (список)
# - create() → POST /users/ (создание)
# - retrieve()→ GET /users/{id}/ (деталь)
# - update() → PUT /users/{id}/ (обновление)
# - destroy() → DELETE /users/{id}/ (удаление)
# - partial_update() → PATCH /users/{id}/
2. Фильтрация и поиск
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# Возможности
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['is_active', 'role'] # Точное совпадение
search_fields = ['name', 'email'] # Поиск
ordering_fields = ['created_at', 'name'] # Сортировка
3. Пермиссии и аутентификация
from rest_framework.permissions import IsAuthenticated, IsAdminUser
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication, SessionAuthentication]
# Разные пермиссии для разных действий
def get_permissions(self):
if self.action == 'destroy':
permission_classes = [IsAdminUser] # Только админ может удалять
else:
permission_classes = [IsAuthenticated] # Остальное для всех
return [permission() for permission in permission_classes]
4. Кастомные методы (@action)
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@action(detail=True, methods=['post'])
def set_password(self, request, pk=None):
"""POST /users/{id}/set_password/"""
user = self.get_object()
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.validated_data['password'])
user.save()
return Response({'status': 'password set'})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['get'])
def recent_users(self, request):
"""GET /users/recent_users/ — список последних пользователей"""
recent = User.objects.all().order_by('-created_at')[:10]
serializer = self.get_serializer(recent, many=True)
return Response(serializer.data)
Типы ViewSet'ов
1. ModelViewSet — полный CRUD
from rest_framework.viewsets import ModelViewSet
class UserViewSet(ModelViewSet):
# Генерирует: list, create, retrieve, update, destroy, partial_update
queryset = User.objects.all()
serializer_class = UserSerializer
Автоматически генерирует:
GET /users/ → list()
POST /users/ → create()
GET /users/{id}/ → retrieve()
PUT /users/{id}/ → update()
PATCH /users/{id}/ → partial_update()
DELETE /users/{id}/ → destroy()
2. ReadOnlyModelViewSet — только чтение
from rest_framework.viewsets import ReadOnlyModelViewSet
class ArticleViewSet(ReadOnlyModelViewSet):
# Только list и retrieve
queryset = Article.objects.all()
serializer_class = ArticleSerializer
Генерирует:
GET /articles/ → list()
GET /articles/{id}/ → retrieve()
3. ViewSet — базовый (нужно реализовать методы)
from rest_framework.viewsets import ViewSet
class UserViewSet(ViewSet):
def list(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
def create(self, request):
serializer = UserSerializer(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)
def retrieve(self, request, pk=None):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
Регистрация в URLs
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'articles', views.ArticleViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
# Автоматически генерирует:
# GET /api/users/
# POST /api/users/
# GET /api/users/{id}/
# PUT /api/users/{id}/
# PATCH /api/users/{id}/
# DELETE /api/users/{id}/
# POST /api/users/{id}/set_password/ ← кастомное действие
Полный пример: Blog API
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_fields = ['author', 'category']
search_fields = ['title', 'content']
def perform_create(self, serializer):
# Автоматически присваиваем текущего пользователя
serializer.save(author=self.request.user)
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
"""POST /articles/{id}/publish/ — опубликовать статью"""
article = self.get_object()
article.published = True
article.published_at = timezone.now()
article.save()
return Response({'status': 'article published'})
@action(detail=True, methods=['get'])
def comments(self, request, pk=None):
"""GET /articles/{id}/comments/ — комментарии статьи"""
article = self.get_object()
comments = article.comments.all()
serializer = CommentSerializer(comments, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def popular(self, request):
"""GET /articles/popular/ — популярные статьи"""
popular_articles = Article.objects.filter(
views__gt=1000
).order_by('-views')[:10]
serializer = self.get_serializer(popular_articles, many=True)
return Response(serializer.data)
Преимущества ViewSet
✅ DRY — пишем один класс вместо нескольких View
✅ Автоматическое роутирование — Router генерирует URLs
✅ Стандартные операции — CRUD из коробки
✅ Кастомизация — легко добавлять кастомные методы
✅ Консистентность — одинаковая API для всех ресурсов
Когда НЕ использовать ViewSet
❌ Когда нужна полная кастомизация (используй APIView)
❌ Когда операции сильно отличаются
❌ Когда логика очень сложная
Итог
ViewSet состоит из:
- Queryset — база данных
- Serializer — валидация и сериализация
- Пермиссии — кто может, что делать
- Фильтрация — поиск, сортировка, фильтры
- Встроенные методы — list, create, retrieve, update, destroy
- Кастомные методы — @action для специальных операций
ViewSet — это стандарт для REST API в Django.