Django REST Framework (DRF)
DRF is a toolkit built on top of Django for building HTTP APIs. It does not replace Django — it sits on the URL/view layer and adds: serializers (validation + (de)serialization), class-based API views, viewsets, routers, content negotiation, auth/permission/throttle classes, pagination, filtering, browsable API.
What DRF gives you on top of Django
| Concern | Plain Django | DRF |
|---|---|---|
| (De)serialization | Manual model_to_dict / JsonResponse |
Serializer, ModelSerializer with validation |
| View base | View, TemplateView |
APIView, GenericAPIView, ViewSet, ModelViewSet |
| URL wiring | path(...) per view |
Router auto-registers CRUD + @action endpoints |
| Auth | Sessions, decorators | authentication_classes (Session, Token, JWT, custom) |
| Permissions | @login_required, @permission_required |
permission_classes per view/action, object-level perms |
| Negotiation | Single response type | Renderer/parser classes choose JSON/HTML/etc. by Accept |
| Browsable API | None | Self-documenting HTML UI in dev |
| Pagination | Manual Paginator |
Pluggable pagination_class (Page/Limit/Cursor) |
| Filtering | Manual request.GET |
filter_backends + django-filter |
Minimal end-to-end example
# models.py
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey("Author", on_delete=models.CASCADE, related_name="books")
# serializers.py
from rest_framework import serializers
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ["id", "title", "author"]
# views.py
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.select_related("author")
serializer_class = BookSerializer
# urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register("books", BookViewSet)
urlpatterns = [path("api/", include(router.urls))]
That single ViewSet wires up: GET /api/books/, POST /api/books/, GET/PUT/PATCH/DELETE /api/books/{pk}/.
Request lifecycle (what happens for every API call)
- URL resolver finds the view (router-generated or manual).
APIView.dispatch()wraps Django’sHttpRequestinto DRF’sRequest.perform_authentication()runs eachauthentication_classesuntil one returns a user (or all returnNone→AnonymousUser).check_permissions()runspermission_classes. Failure → 403/401.check_throttles()runsthrottle_classes. Failure → 429.- Method handler (
get/post/…) runs. Body is parsed byparser_classes. - Returned
Responseis rendered by the negotiatedrenderer_classesbased onAccept.
Memorize this — it’s the spine of “why isn’t my permission/auth running” debugging.
File layout in this folder
| # | File | Topic |
|---|---|---|
| 02 | 02_serializers.md | Serializer vs ModelSerializer, fields, to_representation |
| 03 | 03_validation.md | field-level, object-level, validators, UniqueTogether |
| 04 | 04_views_apiview_viewsets.md | APIView → GenericAPIView → ViewSet ladder |
| 05 | 05_routers_actions.md | Routers, @action, basenames |
| 06 | 06_permissions.md | IsAuthenticated, custom, object-level |
| 07 | 07_authentication.md | Session/Token/JWT, custom auth class |
| 08 | 08_throttling.md | Anon/User/Scoped throttles |
| 09 | 09_pagination.md | PageNumber, LimitOffset, Cursor |
| 10 | 10_filtering_searching.md | SearchFilter, OrderingFilter, django-filter |
| 11 | 11_nested_writable_serializers.md | Writable nested create()/update() |
| 12 | 12_performance_n_plus_1.md | select_related/prefetch_related traps |
| 13 | 13_testing.md | APIClient, force_authenticate |
| 14 | 14_exception_handling.md | exception_handler, custom errors |
| 15 | 15_versioning.md | URL/header/namespace versioning |
| 16 | 16_schema_openapi.md | drf-spectacular, schema generation |
Cross-links to related notes:
- ../05_serializers_actions.md — serializers + actions overview (parent)
- ../11_select_related_vs_prefetch_related.md — ORM optimization
- ../../../07_rest_apis/ — REST principles, status codes, idempotency
Interview angle
- “Why DRF and not just Django views returning JsonResponse?” — auto CRUD via routers/viewsets, declarative validation via serializers, pluggable auth/perm/throttle stack, browsable API for onboarding, content negotiation.
- “Walk me through what happens when a request hits a DRF endpoint.” — the 7-step lifecycle above.
- “What’s the difference between
Requestand Django’sHttpRequest?” — DRF wraps it.request.datais parsed body (JSON/form/multipart unified),request.query_paramsaliasesGET, andrequest.user/request.authcome fromauthentication_classes. - “Where does DRF hurt at scale?” — serializer overhead on huge querysets, hidden N+1 from
SerializerMethodField, defaultModelSerializerdoing extraUNIQUEvalidator queries on writes. See 12_performance_n_plus_1.md.