Django & Flask Topic
REST APIs with Django REST Framework (DRF)
Modern applications often have:
-
A frontend (React, Vue, Angular, or a mobile app)
-
A backend (Django) that handles data and business logic
They need to communicate. APIs provide a standardized way for this communication to happen, typically using JSON data instead of HTML.
DRF makes it incredibly easy to build these APIs by providing:
-
Tools to serialize your Django models into JSON
-
View classes that handle HTTP methods (GET, POST, PUT, DELETE)
-
Authentication and permission systems
-
Browsable APIs that are self-documenting
1. What is Django REST Framework (DRF)?
DRF is a third-party package that builds on top of Django to create powerful, flexible, and standards-compliant Web APIs.
Installation and Setup
bash
pip install djangorestframework
Add it to your INSTALLED_APPS:
python
# settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
'rest_framework.authtoken', # If using token auth
]
2. Key Concepts in Action
Let's build a REST API for our blog, allowing clients to Create, Read, Update, and Delete (CRUD) blog posts.
Serializers
What they are: Serializers convert complex data types (like Django model instances) into Python datatypes that can be easily rendered into JSON. They also handle deserialization, converting JSON data back into model instances.
Think of them as the bridge between your models and JSON.
Creating a Serializer:
python
# serializers.py
from rest_framework import serializers
from .models import BlogPost, Author
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'name', 'email'] # Fields to include in the API
class BlogPostSerializer(serializers.ModelSerializer):
# Nested serializer to include author details
author = AuthorSerializer(read_only=True)
class Meta:
model = BlogPost
fields = ['id', 'title', 'content', 'published_date', 'author', 'categories']
read_only_fields = ['id', 'published_date'] # These are set automatically
APIView Classes
APIViews are the core of DRF. They handle HTTP methods similarly to Django views, but are specifically designed for APIs.
Basic API View:
python
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import BlogPost
from .serializers import BlogPostSerializer
class BlogPostList(APIView):
"""
List all blog posts, or create a new one.
"""
def get(self, request):
posts = BlogPost.objects.all()
serializer = BlogPostSerializer(posts, many=True) # Serialize multiple objects
return Response(serializer.data)
def post(self, request):
serializer = BlogPostSerializer(data=request.data)
if serializer.is_valid():
serializer.save() # This creates the new BlogPost
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Detail API View:
python
class BlogPostDetail(APIView):
"""
Retrieve, update or delete a single blog post.
"""
def get_object(self, pk):
try:
return BlogPost.objects.get(pk=pk)
except BlogPost.DoesNotExist:
return None
def get(self, request, pk):
post = self.get_object(pk)
if post is None:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BlogPostSerializer(post)
return Response(serializer.data)
def put(self, request, pk):
post = self.get_object(pk)
if post is None:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BlogPostSerializer(post, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
post = self.get_object(pk)
if post is None:
return Response(status=status.HTTP_404_NOT_FOUND)
post.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
URL Configuration:
python
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('posts/', views.BlogPostList.as_view(), name='post-list'),
path('posts/<int:pk>/', views.BlogPostDetail.as_view(), name='post-detail'),
]
ViewSets and Routers
ViewSets combine the logic for multiple actions (list, create, retrieve, update, delete) into a single class. Routers automatically generate URL patterns for ViewSets.
This is the most efficient way to build APIs in DRF.
Creating a ViewSet:
python
# views.py
from rest_framework import viewsets
from .models import BlogPost
from .serializers import BlogPostSerializer
class BlogPostViewSet(viewsets.ModelViewSet):
"""
A ViewSet for viewing and editing blog posts.
"""
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
Using a Router:
python
# urls.py
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register(r'posts', views.BlogPostViewSet)
urlpatterns = router.urls
That's it! With just these few lines, you get a complete CRUD API with these endpoints:
-
GET/POST /posts/ -
GET/PUT/DELETE /posts/{id}/
Authentication Tokens
APIs need authentication too. While sessions work for web browsers, tokens are better for mobile apps and JavaScript frontends.
1. Token Authentication (Built-in)
python
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', # Default to requiring auth
]
}
Creating tokens:
python
# When a user registers, create a token for them
from rest_framework.authtoken.models import Token
def create_user_with_token(username, password):
user = User.objects.create_user(username=username, password=password)
token = Token.objects.create(user=user)
return user, token
Using tokens in API calls:
bash
# Include the token in the Authorization header curl -H "Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b" http://localhost:8000/api/posts/
2. JWT (JSON Web Tokens) - More Modern
JWT tokens are stateless and don't require database storage.
bash
pip install djangorestframework-simplejwt
python
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
# urls.py
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'),
]
Using JWT:
python
# Client gets tokens
import requests
response = requests.post('http://localhost:8000/api/token/', {
'username': 'johndoe',
'password': 'supersecret'
})
tokens = response.json() # {'access': 'xxx', 'refresh': 'yyy'}
# Use the access token
headers = {'Authorization': f'Bearer {tokens["access"]}'}
response = requests.get('http://localhost:8000/api/posts/', headers=headers)
Permissions
Control who can access your API endpoints.
python
# views.py
from rest_framework import permissions
class BlogPostViewSet(viewsets.ModelViewSet):
queryset = BlogPost.objects.all()
serializer_class = BlogPostSerializer
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
if self.action == 'list' or self.action == 'retrieve':
permission_classes = [permissions.AllowAny] # Anyone can read
else:
permission_classes = [permissions.IsAuthenticated] # Only authenticated users can write
return [permission() for permission in permission_classes]
def perform_create(self, serializer):
"""Set the author to the current user when creating a post"""
serializer.save(author=self.request.user)
Summary
| Concept | What it is | Purpose |
|---|---|---|
| Serializer | ModelSerializer classes |
Convert between Django models and JSON |
| APIView | Class-based views with HTTP methods | Handle API requests with explicit control |
| ViewSet | ModelViewSet classes |
Combine CRUD operations in one class |
| Router | DefaultRouter() |
Automatically generate URLs for ViewSets |
| Token Auth | TokenAuthentication |
Simple token-based authentication |
| JWT | JWTAuthentication |
Modern, stateless token authentication |
| Permissions | IsAuthenticated, AllowAny |
Control access to API endpoints |
Typical API Flow:
-
Client sends request to
/api/posts/with JWT token in header -
DRF authenticates the token and checks permissions
-
ViewSet retrieves BlogPost objects from database
-
Serializer converts BlogPost objects to JSON
-
DRF returns JSON response to client
Example API Output:
json
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"title": "My First Post",
"content": "This is the content...",
"published_date": "2023-10-25T12:00:00Z",
"author": {
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
}
]
}
DRF makes building professional-grade APIs straightforward and follows Django's "batteries-included" philosophy while being incredibly flexible.
Comments (0)
No comments yet. Be the first to comment!