← Назад к вопросам

Из чего состоит ViewSet

2.3 Middle🔥 121 комментариев
#Django#REST API и HTTP

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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 состоит из:

  1. Queryset — база данных
  2. Serializer — валидация и сериализация
  3. Пермиссии — кто может, что делать
  4. Фильтрация — поиск, сортировка, фильтры
  5. Встроенные методы — list, create, retrieve, update, destroy
  6. Кастомные методы — @action для специальных операций

ViewSet — это стандарт для REST API в Django.