CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-djangorestframework-stubs

PEP 484 type stubs for Django REST Framework enabling static type checking with comprehensive type definitions for all major DRF components

Pending
Overview
Eval results
Files

views-viewsets.mddocs/

Views & ViewSets

Django REST Framework views and viewsets provide the core infrastructure for handling HTTP requests and returning responses. The type stubs enable full type safety for view classes, mixins, and viewset operations with comprehensive generic type support.

Core Imports

from typing import Any, NoReturn, Sequence, Callable
from django.http import HttpRequest, HttpResponseBase
from django.db.models import QuerySet, Manager
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView, UsesQuerySet
from rest_framework.mixins import (
    CreateModelMixin, ListModelMixin, RetrieveModelMixin, 
    UpdateModelMixin, DestroyModelMixin
)
from rest_framework.viewsets import ViewSet, GenericViewSet, ModelViewSet
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.authentication import BaseAuthentication
from rest_framework.permissions import _PermissionClass, _SupportsHasPermission
from rest_framework.renderers import BaseRenderer
from rest_framework.parsers import BaseParser
from rest_framework.throttling import BaseThrottle
from rest_framework.negotiation import BaseContentNegotiation
from rest_framework.metadata import BaseMetadata
from rest_framework.versioning import BaseVersioning
from rest_framework.serializers import BaseSerializer
from rest_framework.filters import BaseFilterBackend
from rest_framework.pagination import BasePagination
from rest_framework.decorators import ViewSetAction

# Type variables
_MT_co = TypeVar("_MT_co", bound=Model, covariant=True)
_ViewFunc = Callable[..., HttpResponseBase]

Base View Classes

APIView

class APIView(View):
    """Base class for all DRF views with comprehensive type safety."""
    
    # Class-level configuration
    authentication_classes: Sequence[type[BaseAuthentication]]
    permission_classes: Sequence[_PermissionClass]
    renderer_classes: Sequence[type[BaseRenderer]]
    parser_classes: Sequence[type[BaseParser]]
    throttle_classes: Sequence[type[BaseThrottle]]
    content_negotiation_class: type[BaseContentNegotiation]
    metadata_class: str | BaseMetadata | None
    versioning_class: type[BaseVersioning] | None
    
    # Request processing
    def dispatch(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def handle_exception(self, exc: Exception) -> Response: ...
    def permission_denied(self, request: Request, message: str | None = None, code: str | None = None) -> NoReturn: ...
    def throttled(self, request: Request, wait: float) -> NoReturn: ...
    
    # Configuration methods
    def get_authenticators(self) -> list[BaseAuthentication]: ...
    def get_permissions(self) -> Sequence[_SupportsHasPermission]: ...
    def get_renderers(self) -> list[BaseRenderer]: ...
    def get_parsers(self) -> list[BaseParser]: ...
    def get_throttles(self) -> list[BaseThrottle]: ...
    def get_content_negotiator(self) -> BaseContentNegotiation: ...
    def get_exception_handler(self) -> Callable: ...
    
    # HTTP method handlers
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def put(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def patch(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def delete(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def head(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def options(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def trace(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

GenericAPIView

class GenericAPIView(APIView, UsesQuerySet[_MT_co]):
    """Generic view with model type support and common functionality."""
    
    # Model and serializer configuration
    queryset: QuerySet[_MT_co] | Manager[_MT_co] | None
    serializer_class: type[BaseSerializer[_MT_co]] | None
    
    # Lookup configuration  
    lookup_field: str  # Default: 'pk'
    lookup_url_kwarg: str | None
    
    # Filtering and pagination
    filter_backends: Sequence[type[BaseFilterBackend | BaseFilterProtocol[_MT_co]]]
    pagination_class: type[BasePagination] | None
    
    # Object retrieval
    def get_object(self) -> _MT_co: ...
    def get_queryset(self) -> QuerySet[_MT_co]: ...
    def filter_queryset(self, queryset: QuerySet[_MT_co]) -> QuerySet[_MT_co]: ...
    def paginate_queryset(self, queryset: QuerySet[_MT_co] | Sequence[Any]) -> Sequence[Any] | None: ...
    def get_paginated_response(self, data: Any) -> Response: ...
    
    # Serializer methods
    def get_serializer(self, *args: Any, **kwargs: Any) -> BaseSerializer[_MT_co]: ...
    def get_serializer_class(self) -> type[BaseSerializer[_MT_co]]: ...
    def get_serializer_context(self) -> dict[str, Any]: ...

Parameters:

  • queryset: QuerySet[_MT_co] | Manager[_MT_co] | None - Base queryset for the view
  • serializer_class: type[BaseSerializer[_MT_co]] | None - Serializer class for the model
  • lookup_field: str - Model field for object lookup (default: 'pk')
  • lookup_url_kwarg: str | None - URL kwarg name for lookup (defaults to lookup_field)

View Mixins

CreateModelMixin

class CreateModelMixin:
    """Mixin for creating model instances."""
    
    def create(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def perform_create(self: UsesQuerySet[_MT], serializer: BaseSerializer[_MT]) -> None: ...
    def get_success_headers(self, data: Any) -> dict[str, str]: ...

ListModelMixin

class ListModelMixin:
    """Mixin for listing model instances."""
    
    def list(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

RetrieveModelMixin

class RetrieveModelMixin:
    """Mixin for retrieving a model instance."""
    
    def retrieve(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

UpdateModelMixin

class UpdateModelMixin:
    """Mixin for updating model instances."""
    
    def update(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def partial_update(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def perform_update(self: UsesQuerySet[_MT], serializer: BaseSerializer[_MT]) -> None: ...

DestroyModelMixin

class DestroyModelMixin:
    """Mixin for deleting model instances."""
    
    def destroy(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def perform_destroy(self: UsesQuerySet[_MT], instance: _MT) -> None: ...

Concrete Generic Views

Create Views

class CreateAPIView(CreateModelMixin, GenericAPIView[_MT_co]):
    """Concrete view for creating model instances."""
    
    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

List Views

class ListAPIView(ListModelMixin, GenericAPIView[_MT_co]):
    """Concrete view for listing model instances."""
    
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

Retrieve Views

class RetrieveAPIView(RetrieveModelMixin, GenericAPIView[_MT_co]):
    """Concrete view for retrieving a model instance."""
    
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

Update Views

class UpdateAPIView(UpdateModelMixin, GenericAPIView[_MT_co]):
    """Concrete view for updating model instances."""
    
    def put(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def patch(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

Delete Views

class DestroyAPIView(DestroyModelMixin, GenericAPIView[_MT_co]):
    """Concrete view for deleting model instances."""
    
    def delete(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

Combined Views

class ListCreateAPIView(ListModelMixin, CreateModelMixin, GenericAPIView[_MT_co]):
    """List and create model instances."""
    
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def post(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

class RetrieveUpdateAPIView(RetrieveModelMixin, UpdateModelMixin, GenericAPIView[_MT_co]):
    """Retrieve and update model instances."""
    
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def put(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def patch(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

class RetrieveDestroyAPIView(RetrieveModelMixin, DestroyModelMixin, GenericAPIView[_MT_co]):
    """Retrieve and delete model instances."""
    
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def delete(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

class RetrieveUpdateDestroyAPIView(
    RetrieveModelMixin, 
    UpdateModelMixin, 
    DestroyModelMixin, 
    GenericAPIView[_MT_co]
):
    """Full CRUD operations for model instances."""
    
    def get(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def put(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def patch(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...
    def delete(self, request: Request, *args: Any, **kwargs: Any) -> Response: ...

ViewSet Classes

ViewSetMixin

class ViewSetMixin:
    """Base mixin that converts a ViewSet class into a concrete view."""
    
    # Class variables assigned in as_view()
    name: str | None
    description: str | None
    suffix: str | None
    detail: bool
    basename: str
    # Instance attributes assigned in view wrapper
    action_map: dict[str, str]
    args: tuple[Any, ...]
    kwargs: dict[str, Any]
    # Assigned in initialize_request()
    action: str
    
    @classmethod
    def as_view(
        cls, 
        actions: dict[str, str | ViewSetAction] | None = None, 
        **initkwargs: Any
    ) -> AsView[GenericView]: ...
    
    def initialize_request(self, request: HttpRequest, *args: Any, **kwargs: Any) -> Request: ...
    def reverse_action(self, url_name: str, *args: Any, **kwargs: Any) -> str: ...
    @classmethod
    def get_extra_actions(cls) -> list[_ViewFunc]: ...
    def get_extra_action_url_map(self) -> dict[str, str]: ...

ViewSet

class ViewSet(ViewSetMixin, APIView):
    """Base ViewSet class providing the interface for routing multiple actions."""
    pass

GenericViewSet

class GenericViewSet(ViewSetMixin, GenericAPIView[_MT_co]):
    """Generic ViewSet with model type support."""
    pass

Model ViewSets

class ReadOnlyModelViewSet(
    RetrieveModelMixin, 
    ListModelMixin, 
    GenericViewSet[_MT_co]
):
    """ViewSet that provides read-only actions."""
    pass

class ModelViewSet(
    CreateModelMixin,
    RetrieveModelMixin, 
    UpdateModelMixin,
    DestroyModelMixin,
    ListModelMixin,
    GenericViewSet[_MT_co]
):
    """ViewSet that provides full CRUD actions."""
    pass

Usage Examples

Custom API View

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated

class BookSearchView(APIView):
    """Custom view for searching books."""
    
    permission_classes = [IsAuthenticated]
    
    def get(self, request: Request) -> Response:
        query = request.query_params.get('q', '')
        if not query:
            return Response(
                {'error': 'Query parameter q is required'}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        books = Book.objects.filter(title__icontains=query)
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
    
    def post(self, request: Request) -> Response:
        """Advanced search with filters."""
        serializer = BookSearchSerializer(data=request.data)
        if serializer.is_valid():
            # Perform complex search logic
            results = self.perform_search(serializer.validated_data)
            return Response(results)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def perform_search(self, filters: dict[str, Any]) -> list[dict[str, Any]]:
        """Perform the actual search operation."""
        queryset = Book.objects.all()
        
        if 'genre' in filters:
            queryset = queryset.filter(genre=filters['genre'])
        if 'author' in filters:
            queryset = queryset.filter(author__name__icontains=filters['author'])
        if 'published_after' in filters:
            queryset = queryset.filter(published_date__gte=filters['published_after'])
        
        serializer = BookSerializer(queryset, many=True)
        return serializer.data

Generic List Create View

from rest_framework import generics
from rest_framework.permissions import IsAuthenticated

class BookListCreateView(generics.ListCreateAPIView[Book]):
    """List books and create new books."""
    
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [IsAuthenticated]
    
    def get_queryset(self) -> QuerySet[Book]:
        """Filter books by current user if requested."""
        queryset = super().get_queryset()
        user_only = self.request.query_params.get('user_only', 'false')
        
        if user_only.lower() == 'true':
            return queryset.filter(created_by=self.request.user)
        return queryset
    
    def perform_create(self, serializer: BookSerializer) -> None:
        """Set the created_by field to current user."""
        serializer.save(created_by=self.request.user)

Model ViewSet

from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

class BookViewSet(viewsets.ModelViewSet[Book]):
    """Full CRUD operations for books."""
    
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    def get_permissions(self) -> list[BasePermission]:
        """Different permissions for different actions."""
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            permission_classes = [IsAuthenticated]
        else:
            permission_classes = []
        return [permission() for permission in permission_classes]
    
    def get_serializer_class(self) -> type[BaseSerializer[Book]]:
        """Different serializers for different actions."""
        if self.action == 'list':
            return BookListSerializer
        elif self.action in ['create', 'update', 'partial_update']:
            return BookWriteSerializer
        return BookDetailSerializer
    
    @action(detail=True, methods=['post'])
    def favorite(self, request: Request, pk: str | None = None) -> Response:
        """Add book to user's favorites."""
        book = self.get_object()
        user = request.user
        
        if user in book.favorited_by.all():
            return Response(
                {'detail': 'Already favorited'}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        book.favorited_by.add(user)
        return Response({'detail': 'Added to favorites'})
    
    @action(detail=True, methods=['delete'])
    def unfavorite(self, request: Request, pk: str | None = None) -> Response:
        """Remove book from user's favorites."""
        book = self.get_object()
        user = request.user
        
        if user not in book.favorited_by.all():
            return Response(
                {'detail': 'Not in favorites'}, 
                status=status.HTTP_400_BAD_REQUEST
            )
        
        book.favorited_by.remove(user)
        return Response({'detail': 'Removed from favorites'})
    
    @action(detail=False, methods=['get'])
    def popular(self, request: Request) -> Response:
        """Get popular books based on favorites count."""
        popular_books = Book.objects.annotate(
            favorites_count=Count('favorited_by')
        ).order_by('-favorites_count')[:10]
        
        serializer = self.get_serializer(popular_books, many=True)
        return Response(serializer.data)

Custom ViewSet Actions

from rest_framework.decorators import action

class AuthorViewSet(viewsets.ModelViewSet[Author]):
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer
    
    @action(detail=True, methods=['get'])
    def books(self, request: Request, pk: str | None = None) -> Response:
        """Get all books by this author."""
        author = self.get_object()
        books = author.book_set.all()
        serializer = BookSerializer(books, many=True)
        return Response(serializer.data)
    
    @action(detail=True, methods=['get'])
    def statistics(self, request: Request, pk: str | None = None) -> Response:
        """Get author statistics."""
        author = self.get_object()
        stats = {
            'total_books': author.book_set.count(),
            'average_rating': author.book_set.aggregate(
                avg_rating=Avg('rating')
            )['avg_rating'] or 0,
            'latest_book': author.book_set.order_by('-published_date').first().title
        }
        return Response(stats)
    
    @action(detail=False, methods=['get'], url_path='by-genre/(?P<genre>[^/.]+)')
    def by_genre(self, request: Request, genre: str | None = None) -> Response:
        """Get authors who have written books in a specific genre."""
        authors = Author.objects.filter(book__genre=genre).distinct()
        serializer = self.get_serializer(authors, many=True)
        return Response(serializer.data)

View Configuration

Permissions and Authentication

class ProtectedBookView(generics.RetrieveUpdateDestroyAPIView[Book]):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    authentication_classes = [TokenAuthentication, SessionAuthentication]
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    
    def get_permissions(self) -> list[BasePermission]:
        """Custom permission logic based on action."""
        if self.request.method in ['PUT', 'PATCH', 'DELETE']:
            permission_classes = [IsAuthenticated, IsOwner]
        else:
            permission_classes = [AllowAny]
        return [permission() for permission in permission_classes]

Filtering and Pagination

from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend

class BookListView(generics.ListAPIView[Book]):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['genre', 'author', 'published_date']
    search_fields = ['title', 'description', 'author__name']
    ordering_fields = ['title', 'published_date', 'rating']
    ordering = ['-published_date']
    pagination_class = PageNumberPagination

Utility Functions

View Helper Functions

def get_view_name(view: APIView) -> str:
    """Get a human-readable name for the view."""
    ...

def get_view_description(view: APIView, html: bool = False) -> str:
    """Get a human-readable description for the view."""
    ...

def set_rollback() -> None:
    """Mark the current transaction as rollback-only."""
    ...

def exception_handler(exc: Exception, context: dict[str, Any]) -> Response | None:
    """Default exception handler for DRF views."""
    ...

Performance Optimization

Query Optimization

class OptimizedBookViewSet(viewsets.ModelViewSet[Book]):
    serializer_class = BookSerializer
    
    def get_queryset(self) -> QuerySet[Book]:
        """Optimize queries with select_related and prefetch_related."""
        return Book.objects.select_related('author', 'publisher') \
                          .prefetch_related('genres', 'reviews')
    
    def list(self, request: Request, *args: Any, **kwargs: Any) -> Response:
        """Custom list method with query optimization."""
        queryset = self.get_queryset()
        
        # Add any additional filtering
        search = request.query_params.get('search')
        if search:
            queryset = queryset.filter(
                Q(title__icontains=search) | 
                Q(description__icontains=search)
            )
        
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

Caching

from django.core.cache import cache
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page

class CachedBookListView(generics.ListAPIView[Book]):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    @method_decorator(cache_page(60 * 15))  # Cache for 15 minutes
    def dispatch(self, *args: Any, **kwargs: Any) -> Response:
        return super().dispatch(*args, **kwargs)
    
    def list(self, request: Request, *args: Any, **kwargs: Any) -> Response:
        cache_key = f"book_list_{request.GET.urlencode()}"
        cached_response = cache.get(cache_key)
        
        if cached_response is not None:
            return Response(cached_response)
        
        response = super().list(request, *args, **kwargs)
        cache.set(cache_key, response.data, timeout=60 * 15)
        return response

This comprehensive view and viewset system provides type-safe HTTP request handling with full generic type support, enabling confident API development with complete mypy integration.

Install with Tessl CLI

npx tessl i tessl/pypi-djangorestframework-stubs

docs

authentication-permissions.md

exceptions-status.md

fields-relations.md

index.md

pagination-filtering.md

requests-responses.md

routers-urls.md

serializers.md

views-viewsets.md

tile.json