Django REST Framework

Django REST Framework (DRF) is the standard toolkit for building REST APIs with Django. It adds serializers that convert model instances to JSON and validate incoming data, class-based views and ViewSets that implement CRUD endpoints in a few lines, routers that generate URLs, and pluggable authentication, permissions, throttling, filtering, and pagination. Its browsable API makes endpoints explorable in a web browser.

DRF's layered design lets you start with a ModelViewSet that does everything, then override exactly the parts you need. The main pitfalls are performance (serializers that trigger N+1 queries) and security (permissions that don't cover object-level access).

TL;DR

Quick Example

This yields GET/POST /orders/, GET/PUT/PATCH/DELETE /orders/{id}/, and POST /orders/{id}/cancel/.

Core Concepts

Serializers

Serializers work in both directions:

Validation layers: field types and options, validate_<field>() methods, object-level validate(), and validators like UniqueTogetherValidator. Use separate read and write serializers when the input and output shapes differ significantly. Nested writes aren't automatic; implement create()/update() explicitly for them.

Views: From APIView to ViewSets

Override hooks like get_queryset(), get_serializer_class(), perform_create(), and get_permissions() to customize behavior without rewriting handlers.

Authentication and Permissions

Object permissions run only when the view calls get_object(). List views must restrict data through get_queryset(), or users can list objects they shouldn't see.

Filtering, Pagination, and Throttling

Pagination options are PageNumberPagination, LimitOffsetPagination, and CursorPagination (stable and efficient for large, changing datasets). For rate limiting at scale, back throttles with a shared cache like Redis.

API Documentation

drf-spectacular generates an OpenAPI 3 schema from your views and serializers, served through Swagger UI or ReDoc. Refine it with @extend_schema decorators. The schema also powers client code generation. See API documentation.

Best Practices

Scope Querysets to the User

Make get_queryset() return only what the requesting user may access. It protects list, detail, update, and delete endpoints at once, and complements object-level permissions.

Optimize Serializer Queries

Nested serializers and source="relation.field" trigger lazy loads per object. Add matching select_related/prefetch_related in get_queryset(), and test query counts with assertNumQueries. See Django ORM.

Keep Business Logic Out of Serializers and Views

Serializers validate and transform; views handle HTTP. Put domain operations (cancelling orders, charging payments) in model methods or service functions that views call, which keeps them reusable from admin, tasks, and management commands.

Set Secure Defaults Globally

Default to IsAuthenticated, and opt out explicitly (AllowAny) for public endpoints. That's safer than remembering to protect each new view.

Common Mistakes

Exposing Every Model Field

Relying on Object Permissions for List Views

has_object_permission isn't called for list endpoints or custom queries. Without a filtered get_queryset(), GET /orders/ returns everyone's orders.

Heavy Logic in SerializerMethodField

A SerializerMethodField that runs a query per object is an N+1 in disguise. Precompute values with annotate() in the queryset and expose them as plain fields.

FAQ

Should I use DRF or Django Ninja?

DRF is mature, feature-rich, and widely known, with a large ecosystem of extensions. Django Ninja offers a FastAPI-like, type-hint-driven style with Pydantic schemas, automatic OpenAPI, and good async support. Both are solid choices. DRF is the conservative default, and Ninja is attractive for new, type-heavy projects.

How do I do JWT authentication with DRF?

Use djangorestframework-simplejwt: add its authentication class, and expose token obtain and refresh endpoints. Keep access tokens short-lived, and consider session authentication for first-party browser apps, where cookies with CSRF protection are simpler and safer than tokens in JavaScript.

How do I version a DRF API?

DRF supports URL path, namespace, header (Accept), and query-parameter versioning. request.version then lets views choose serializers. Many teams version only at the URL prefix (/api/v1/) and evolve additively within a version. See API versioning.

Does DRF support async views?

DRF's core is synchronous. It runs fine under ASGI, but views execute synchronously. For async-heavy APIs, consider Django's native async views, Django Ninja, or adapters like adrf. See async Django.

Related Topics

References