Django REST Framework Basics
Introduction
Django REST framework (DRF) builds JSON APIs on top of Django—serializers replace manual JsonResponse dicts, and ViewSets plus routers replace repetitive URL wiring. This chapter installs DRF, exposes /api/v1/posts/ CRUD for the Post model, adds pagination, and introduces permission classes. API authentication and CORS are covered in the next chapter.
Prerequisites
- Models and ORM Basics —
Postmodel migrated - URLs and Routing
- Testing Django Applications — optional for API tests
Install and Configure
pip install djangorestframeworkAdd to requirements.txt.
config/settings/base.py:
Browsable API and JSON renderers are enabled by default—great for learning; tighten permissions in production.
Serializer
blog/serializers.py:
Code explanation:
ModelSerializergenerates fields from the modelsource="author.username"flattens related data for JSONread_only_fields— client cannot setauthordirectly
Validation
def validate_slug(self, value):
if value == "admin":
raise serializers.ValidationError("Reserved slug.")
return valueField validators mirror Django forms; model clean() also runs on save().
APIView (Explicit)
blog/api_views.py:
Response sets status and content negotiation (JSON by default).
ModelViewSet and Router (Recommended)
blog/viewsets.py:
blog/urls_api.py:
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .viewsets import PostViewSet
router = DefaultRouter()
router.register("posts", PostViewSet, basename="post")
urlpatterns = [
path("", include(router.urls)),
]config/urls.py:
urlpatterns = [
path("admin/", admin.site.urls),
path("api/v1/", include("blog.urls_api")),
path("", include("blog.urls")),
]Generated routes:
| Method | URL | Action |
|---|---|---|
| GET | /api/v1/posts/ | list |
| POST | /api/v1/posts/ | create |
| GET | /api/v1/posts/{id}/ | retrieve |
| PUT/PATCH | /api/v1/posts/{id}/ | update |
| DELETE | /api/v1/posts/{id}/ | destroy |
Try the API
python manage.py runserverList:
curl http://127.0.0.1:8000/api/v1/posts/Create (requires auth in next chapter—for now with AllowAny and existing user):
curl -X POST http://127.0.0.1:8000/api/v1/posts/ \
-H "Content-Type: application/json" \
-d '{"title":"API Post","slug":"api-post","body":"Hello DRF","published":true}'Browse http://127.0.0.1:8000/api/v1/posts/ in browser for DRF’s HTML browsable API (dev only).
Pagination
With PageNumberPagination and PAGE_SIZE: 10:
curl "http://127.0.0.1:8000/api/v1/posts/?page=2"Response shape:
{
"count": 25,
"next": "http://127.0.0.1:8000/api/v1/posts/?page=3",
"previous": "http://127.0.0.1:8000/api/v1/posts/?page=1",
"results": [...]
}Filtering (Concept)
Install django-filter:
pip install django-filterREST_FRAMEWORK = {
...
"DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend",
],
}
class PostViewSet(viewsets.ModelViewSet):
filterset_fields = ["published", "author"]Query: /api/v1/posts/?author=1&published=true.
Permissions Preview
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class PostViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly]Anonymous users: read only. Authenticated: create/update/delete (with object-level rules added in chapter 20).
Compare with Spring Boot REST
| DRF | Spring Boot |
|---|---|
ModelSerializer | DTO + MapStruct / manual mapping |
ModelViewSet | @RestController + service layer |
DefaultRouter | @RequestMapping per method |
| Browsable API | Springdoc OpenAPI UI |
See Spring Data JPA REST patterns and Spring Security JWT.
FastAPI alternative: FastAPI track for async-first APIs.
API Tests
blog/tests/test_api.py:
Use APITestCase and format="json" for DRF clients.
Common Mistakes
| Mistake | Symptom | Fix |
|---|---|---|
Forgot rest_framework in apps | Import errors | Add to INSTALLED_APPS |
Writable author field | Users impersonate others | read_only_fields + perform_create |
| N+1 in list API | Slow responses | select_related in get_queryset |
| CSRF on session auth POST | 403 from SPA | Token/JWT auth or CSRF header — ch. 20 |
| Exposing draft posts | Data leak | Filter published=True on list |
Post-Chapter Checklist
- DRF installed and
REST_FRAMEWORKconfigured -
PostSerializerandPostViewSetwired at/api/v1/ - List/detail/create work via curl or browsable API
- Pagination returns
countandresults - You know
APIViewvsModelViewSettradeoffs
FAQ## FAQ
DRF vs plain JsonResponse?
DRF gives validation, serialization, auth, pagination, and browsable API—worth it for non-trivial APIs.
API versioning?
URL prefix /api/v1/ (this track), or header versioning—stay consistent.
Nested routes /posts/1/comments/?
Nested routers (drf-nested-routers) or separate ViewSets with filter.
OpenAPI schema?
drf-spectacular generates Swagger—similar to Springdoc.
Disable browsable API in prod?
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
}