mrkeyoor.com_
Sat 08 Aug 17:41 UTC
PyPIWeb Backendupdated 08 Aug 2026

djangorestframework

API toolkit layered on Django. Serializers validate input and convert models or plain Python objects, APIView adds content negotiation and policy hooks, generic views implement common CRUD flows, and viewsets plus routers generate related endpoints. Authentication, permissions, throttling, pagination, filtering, parsers, renderers, a browsable HTML API, and test clients use Django's ORM, settings, middleware, cache, and request lifecycle. It is the comprehensive Django-native choice, not a standalone ASGI framework or automatic API security boundary.

Verdict

The default serious API toolkit for a Django application, with unusually complete policies and documentation. Do not choose it merely for a small JSON service, and never confuse generated CRUD, throttles, or serializer validation with a finished security design.

API stability4/5Serializers, APIView, generic views, viewsets, routers, authentication, permissions, pagination, and testing have long-lived contracts. Version 3.18.0 still contains real breaking changes: it dropped three Django series and changed list-serializer validation errors from list to dict shape. Application APIs can remain stable, but framework upgrades need response-contract tests.
Docs5/5The official site has a tutorial plus detailed guides for requests, responses, views, serializers, fields, relations, validators, authentication, permissions, caching-based throttles, filtering, pagination, versioning, testing, schemas, and third-party packages. It explicitly describes throttle races and security limits, which is the kind of caveat production users need.
Maintenance5/5Version 3.18.0 was released and the repository was pushed on August 7, 2026. That release added Django 6.1 support, removed unsupported Django lines, improved accessibility, and shipped multiple validation and compatibility fixes. The repository is not archived, maintains a security policy, and has a comparatively small public issues and pull-requests backlog for its age and reach.
Ecosystem5/5It sees roughly 6,626,456 weekly downloads and the repository has 30,125 stars. DRF has mature integrations for filtering, OAuth, JWT, OpenAPI, nested resources, audit logs, object permissions, CORS middleware, and Django's database and deployment ecosystem. It is the convention most Django teams and libraries document first for APIs.

Use it if

  • Your application already uses Django models, authentication, admin, settings, and migrations, and the API should share those rules
  • You need conventional CRUD endpoints quickly but still want explicit serializer, permission, filter, and queryset hooks
  • The browsable API will materially help internal users and backend developers inspect and exercise endpoints
  • You have complex validation or representation rules that benefit from dedicated serializer classes rather than schema-only generation
Skip it if

Setup reality

Version 3.18.0 requires Python 3.10+ and Django 5.2, 6.0, or 6.1, with the project recommending the latest patch of each series. Install djangorestframework, add rest_framework to INSTALLED_APPS, include API URLs, and run Django checks and migrations. That only makes the framework available; production safety depends on REST_FRAMEWORK defaults. Define authentication and permission classes globally or per view because an omitted permission policy can expose an endpoint. SessionAuthentication uses Django's session and CSRF model, which is good for same-site browser clients but surprises JavaScript callers that omit the CSRF token. Built-in TokenAuthentication needs rest_framework.authtoken in INSTALLED_APPS and a migration, and its simple database tokens may not meet rotation, expiry, or OAuth requirements. CORS is not supplied by DRF; configure it in Django middleware with a separate maintained package if browser origins differ. ModelViewSet can expose list, retrieve, create, update, partial_update, and destroy at once, so restrict http_method_names or use narrower generic views rather than assuming the router is read-only. Querysets must be scoped to request.user before object lookup, and custom actions need the same permission discipline. ModelSerializer infers fields and validators from models, but explicit fields are safer than __all__. Avoid N+1 responses by applying select_related and prefetch_related in get_queryset; serializers do not optimize ORM access. List endpoints need pagination and bounded filters before launch. If using DjangoFilterBackend, install and configure django-filter separately. Built-in throttles require a shared cache in multi-process deployments and are policy controls, not DDoS protection. DRF 3.18 changed many=True validation errors from list to dict form, so clients and tests that parse error bodies need migration work. Decide how exception responses, schema generation, versioning, and browsable API exposure work before declaring the API stable.

Patterns

Serialize and validate a modeldefine-model-serializer

from rest_framework import serializers
from .models import Project

class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = ['id', 'name', 'owner', 'created_at']
        read_only_fields = ['id', 'owner', 'created_at']

List fields explicitly. read_only_fields prevents clients from assigning ownership or server-managed values.

Add field and object validationvalidate-input

class ProjectSerializer(serializers.ModelSerializer):
    def validate_name(self, value):
        value = value.strip()
        if not value:
            raise serializers.ValidationError('Name cannot be blank.')
        return value

    def validate(self, attrs):
        if attrs.get('starts_at') and attrs.get('ends_at') and attrs['starts_at'] >= attrs['ends_at']:
            raise serializers.ValidationError({'ends_at': 'Must be after starts_at.'})
        return attrs

On partial updates, attrs may omit either field; read existing instance values when cross-field rules must still apply.

Expose owned records with a viewsetcreate-viewset

from rest_framework import permissions, viewsets

class ProjectViewSet(viewsets.ModelViewSet):
    serializer_class = ProjectSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        return Project.objects.filter(owner=self.request.user).select_related('owner')

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

Scope get_queryset before object lookup. Object permissions alone do not automatically filter list querysets.

Generate routes for a viewsetregister-router

from django.urls import include, path
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('projects', ProjectViewSet, basename='project')

urlpatterns = [path('api/', include(router.urls))]

ModelViewSet generates write and delete routes too. Use ReadOnlyModelViewSet or narrower mixins when mutation is not intended.

Write an explicit APIViewbuild-api-view

from rest_framework import permissions, status, views
from rest_framework.response import Response

class HealthView(views.APIView):
    permission_classes = [permissions.AllowAny]

    def get(self, request):
        return Response({'status': 'ok'}, status=status.HTTP_200_OK)

APIView dispatch is synchronous in the current core. Keep slow external I/O out of this handler or move it to jobs.

Create a bounded list and create endpointuse-generic-view

from rest_framework import generics, permissions

class ProjectList(generics.ListCreateAPIView):
    serializer_class = ProjectSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        return Project.objects.filter(owner=self.request.user).order_by('-created_at')

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

ListCreateAPIView exposes only GET and POST, which is safer than starting with every ModelViewSet action.

Add a permission-aware custom actionadd-viewset-action

from rest_framework.decorators import action
from rest_framework.response import Response

class ProjectViewSet(viewsets.ModelViewSet):
    @action(detail=True, methods=['post'], permission_classes=[permissions.IsAdminUser])
    def archive(self, request, pk=None):
        project = self.get_object()
        project.archive()
        return Response({'status': 'archived'})

get_object applies queryset scoping and object permission checks; do not replace it with an unrestricted model lookup.

Add exact, search, and ordering filtersconfigure-filtering

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

class ProjectViewSet(viewsets.ModelViewSet):
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['status']
    search_fields = ['name', 'description']
    ordering_fields = ['created_at', 'name']
    ordering = ['-created_at']

Install django-filter for DjangoFilterBackend and allowlist ordering fields so clients cannot sort on sensitive or costly columns.

Set a capped page sizeconfigure-pagination

from rest_framework.pagination import PageNumberPagination

class ProjectPagination(PageNumberPagination):
    page_size = 50
    page_size_query_param = 'page_size'
    max_page_size = 200

class ProjectViewSet(viewsets.ModelViewSet):
    pagination_class = ProjectPagination

Always cap client-selected page sizes; serialization and related queries can make an unbounded page expensive.

Set safe application defaultsconfigure-global-policies

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 50,
}

SessionAuthentication requires CSRF protection for unsafe browser requests. Override AllowAny only on intentionally public views.

Throttle an expensive actionapply-business-throttle

from rest_framework.throttling import UserRateThrottle

class ExportThrottle(UserRateThrottle):
    scope = 'exports'

class ExportView(views.APIView):
    throttle_classes = [ExportThrottle]

REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {'exports': '10/hour'},
}

DRF throttles use non-atomic cache operations and are not DDoS or brute-force protection. Use shared cache for consistent policy across processes.

Test permissions and response shapetest-authenticated-endpoint

from rest_framework import status
from rest_framework.test import APITestCase

class ProjectApiTests(APITestCase):
    def test_user_sees_only_owned_projects(self):
        self.client.force_authenticate(user=self.user)
        response = self.client.get('/api/projects/')
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual([item['id'] for item in response.data['results']], [self.project.id])

force_authenticate bypasses the real authentication mechanism; keep separate integration tests for headers, tokens, sessions, and CSRF.

Alternatives

PackageRegistryPick it when
django-ninjaPyPIYou want Django integration with type-hint-driven validation and OpenAPI closer to FastAPI's style
fastapiPyPIYou are building an async-first standalone service and want OpenAPI generated directly from typed endpoints
flask-restxPyPIYou already use Flask and want resource routing, request models, and Swagger documentation without adopting Django