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

Install and Configure

bash
pip install djangorestframework

Add 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:

  • ModelSerializer generates fields from the model
  • source="author.username" flattens related data for JSON
  • read_only_fields — client cannot set author directly

Validation

python
def validate_slug(self, value):
    if value == "admin":
        raise serializers.ValidationError("Reserved slug.")
    return value

Field 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).

blog/viewsets.py:

blog/urls_api.py:

python
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:

python
urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/v1/", include("blog.urls_api")),
    path("", include("blog.urls")),
]

Generated routes:

MethodURLAction
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

bash
python manage.py runserver

List:

bash
curl http://127.0.0.1:8000/api/v1/posts/

Create (requires auth in next chapter—for now with AllowAny and existing user):

bash
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:

bash
curl "http://127.0.0.1:8000/api/v1/posts/?page=2"

Response shape:

json
{
  "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:

bash
pip install django-filter
python
REST_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

python
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

DRFSpring Boot
ModelSerializerDTO + MapStruct / manual mapping
ModelViewSet@RestController + service layer
DefaultRouter@RequestMapping per method
Browsable APISpringdoc 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

MistakeSymptomFix
Forgot rest_framework in appsImport errorsAdd to INSTALLED_APPS
Writable author fieldUsers impersonate othersread_only_fields + perform_create
N+1 in list APISlow responsesselect_related in get_queryset
CSRF on session auth POST403 from SPAToken/JWT auth or CSRF header — ch. 20
Exposing draft postsData leakFilter published=True on list

Post-Chapter Checklist

  • DRF installed and REST_FRAMEWORK configured
  • PostSerializer and PostViewSet wired at /api/v1/
  • List/detail/create work via curl or browsable API
  • Pagination returns count and results
  • You know APIView vs ModelViewSet tradeoffs

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?

python
REST_FRAMEWORK = {
    "DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
}