Python Django REST Framework (DRF) tutorial – If you’re a Python developer looking to build robust, scalable APIs quickly, Django REST Framework (DRF) is the tool you’ve been waiting for. In this comprehensive guide we’ll walk you through everything from setting up a fresh Django project to mastering serializers, viewsets, routers, authentication, pagination, testing, and deployment. By the end of this tutorial you’ll have a production‑ready RESTful API that you can extend for any web or mobile application.
Why Choose Django REST Framework?
DRF sits on top of Django, leveraging its mature ORM, authentication system, and admin interface while adding powerful API‑specific features. Here are a few reasons why DRF is a top choice for Python developers:
- Browsable API: Instantly explore your endpoints in a web browser.
- Serialization: Convert complex Django models to JSON, XML, or other formats with minimal code.
- Viewsets & Routers: Reduce boilerplate by automatically generating CRUD routes.
- Authentication & Permissions: Built‑in support for token, session, OAuth, and custom schemes.
- Extensible: Plug‑in pagination, throttling, and custom renderers as needed.
Getting Started: Project Setup
1. Install Django and DRF
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install django djangorestframework
2. Create a New Django Project
django-admin startproject myapi
cd myapi
python manage.py startapp blog
3. Register Apps in settings.py
Add 'rest_framework' and your new app ('blog') to INSTALLED_APPS:
INSTALLED_APPS = [
# Django defaults …
'rest_framework',
'blog',
]
4. Configure a Simple Global DRF Setting
For a quick start, enable the browsable API and set default pagination:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}
Modeling Your Data
Let’s build a tiny blog API. First, define a Post model inside blog/models.py:
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Run migrations:
python manage.py makemigrations
python manage.py migrate
Serializers: Turning Models into JSON
Serializers are the heart of DRF. They define how model instances are converted to native Python datatypes and then rendered as JSON.
Creating a PostSerializer
# blog/serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
author = serializers.ReadOnlyField(source='author.username')
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'created_at']
Views: Handling Requests
DRF offers several view styles. For a CRUD API, ModelViewSet is the most concise.
Define a PostViewSet
# blog/views.py
from rest_framework import viewsets, permissions
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all().order_by('-created_at')
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
# Automatically assign the logged‑in user as the author
serializer.save(author=self.request.user)
Routing: Connecting URLs to Views
DRF’s DefaultRouter automatically creates the standard list, create, retrieve, update, and destroy routes.
Register the ViewSet
# myapi/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from blog.views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(router.urls)),
path('api-auth/', include('rest_framework.urls')), # browsable API login
]
Authentication & Permissions
Secure your API with token authentication or JWT. Below is a quick setup for DRF’s built‑in token auth.
Install Token Auth
pip install djangorestframework-simplejwt
Add the authentication classes to settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
# other settings …
}
Create Token Endpoints
# myapi/urls.py (add)
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns += [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
Now clients can obtain a JWT by POSTing {"username": "user", "password": "pass"} to /api/token/ and include the token in the Authorization: Bearer <token> header for protected endpoints.
Advanced Features
Custom Pagination
Sometimes you need cursor‑based pagination for large data sets. Define a custom paginator:
# blog/pagination.py
from rest_framework.pagination import CursorPagination
class PostCursorPagination(CursorPagination):
page_size = 10
ordering = '-created_at'
Then apply it to the viewset:
class PostViewSet(viewsets.ModelViewSet):
# …
pagination_class = PostCursorPagination
Filtering and Searching
DRF integrates with django-filter for powerful query‑parameter filtering.
pip install django-filter
Update settings.py and the viewset:
# settings.py
REST_FRAMEWORK['DEFAULT_FILTER_BACKENDS'] = ['django_filters.rest_framework.DjangoFilterBackend',
'rest_framework.filters.SearchFilter']
# blog/views.py
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
class PostViewSet(viewsets.ModelViewSet):
# …
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
filterset_fields = ['author__username']
search_fields = ['title', 'content']
Versioning Your API
Maintain backward compatibility by versioning URLs:
# myapi/urls.py
from rest_framework.versioning import URLPathVersioning
router = DefaultRouter()
router.register(r'posts', PostViewSet, basename='post')
urlpatterns = [
path('api/v1/', include((router.urls, 'v1'), namespace='v1')),
# future versions can be added as /api/v2/ …
]
Testing Your API
DRF provides an APIClient that works like Django’s test client but handles JSON automatically.
Sample Test Case
# blog/tests.py
from django.urls import reverse
from rest_framework.test import APITestCase
from rest_framework import status
from django.contrib.auth import get_user_model
from .models import Post
User = get_user_model()
class PostAPITests(APITestCase):
def setUp(self):
self.user = User.objects.create_user(username='tester', password='secret')
self.client.login(username='tester', password='secret')
self.post = Post.objects.create(
title='First Post',
content='Hello DRF!',
author=self.user
)
def test_list_posts(self):
url = reverse('v1:post-list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 1)
def test_create_post(self):
url = reverse('v1:post-list')
data = {'title': 'New Post', 'content': 'Testing create'}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Post.objects.count(), 2)
Deploying a DRF API to Production
- Use a WSGI server: Gunicorn or uWSGI behind Nginx.
- Static & Media Files: Serve via Nginx or a CDN.
- Environment Variables: Keep secret keys and DB credentials out of source control.
Leave a Reply