Django: Combining Django with REST APIs
Combining Django with REST APIs, typically using Django REST Framework (DRF), enables developers to build powerful, scalable backend services for web and mobile applications. Django handles server-side logic, database management, and authentication, while DRF provides tools to create RESTful APIs that communicate with frontends like React, Vue.js, or mobile apps. This tutorial explores Django with REST APIs, covering setup, API development, and practical applications.
01. Why Combine Django with REST APIs?
REST APIs allow Django to serve data to diverse clients, enabling dynamic applications like single-page applications (SPAs), mobile apps, or third-party integrations. DRF simplifies API creation with serialization, authentication, and viewsets, making Django ideal for e-commerce, social platforms, or IoT systems. This approach separates backend and frontend, improving maintainability and scalability.
Example: Basic API Endpoint
# myapp/views.py
from rest_framework import viewsets
from .models import Item
from .serializers import ItemSerializer
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
Output:
API endpoint at http://127.0.0.1:8000/api/items/ returns item list
Explanation:
viewsets.ModelViewSet
- Provides CRUD operations for the Item model.- DRF handles JSON serialization and HTTP responses.
02. Core Concepts for REST APIs in Django
Django with DRF creates RESTful APIs that follow HTTP standards, supporting GET, POST, PUT, DELETE, and more. DRF extends Django’s capabilities with serialization, authentication, and routing. Below is a summary of key components and their roles:
Component | Description | Use Case |
---|---|---|
Serializers | Convert models to JSON | Data validation, formatting |
Viewsets | Handle API logic | CRUD operations |
Routers | Map URLs to viewsets | Automatic URL routing |
Authentication | Secure API endpoints | User access control |
2.1 Setting Up Django with DRF
Example: DRF Setup
# Install required packages
pip install django djangorestframework django-cors-headers
# myproject/settings.py
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'myapp',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # Add at the top
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ALLOWED_ORIGINS = [
'http://localhost:3000', # Frontend URL (e.g., React/Vue.js)
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
],
}
# myapp/models.py
from django.db import models
class Item(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
# myapp/serializers.py
from rest_framework import serializers
from .models import Item
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ['id', 'name', 'description', 'created_at']
# myapp/views.py
from rest_framework import viewsets
from .models import Item
from .serializers import ItemSerializer
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ItemViewSet
router = DefaultRouter()
router.register(r'items', ItemViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
]
# Apply migrations
python manage.py makemigrations
python manage.py migrate
Output:
API endpoints available at http://127.0.0.1:8000/api/items/
Explanation:
djangorestframework
- Enables API functionality.django-cors-headers
- Allows cross-origin requests from frontend.DefaultRouter
- Automatically generates URLs for viewsets.
2.2 Creating a Simple API Endpoint
Example: CRUD API for Items
# myapp/views.py
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Item
from .serializers import ItemSerializer
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
@action(detail=False, methods=['get'])
def recent(self, request):
recent_items = self.queryset.order_by('-created_at')[:5]
serializer = self.get_serializer(recent_items, many=True)
return Response(serializer.data)
# Test API with curl
curl http://127.0.0.1:8000/api/items/
curl http://127.0.0.1:8000/api/items/recent/
Output:
[
{"id": 1, "name": "Item 1", "description": "Desc", "created_at": "2025-05-16T17:00:00Z"},
{"id": 2, "name": "Item 2", "description": "", "created_at": "2025-05-16T17:01:00Z"}
]
Explanation:
ModelViewSet
- Provides list, create, retrieve, update, delete actions.@action
- Custom endpoint for recent items.
2.3 Adding Authentication with JWT
Example: JWT Authentication
# Install JWT package
pip install djangorestframework-simplejwt
# myproject/settings.py
INSTALLED_APPS = [
...
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
# myproject/urls.py
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
# myapp/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Item
from .serializers import ItemSerializer
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
permission_classes = [IsAuthenticated] # Require JWT
# Test authentication
curl -X POST http://127.0.0.1:8000/api/token/ -d "username=user&password=pass"
curl -H "Authorization: Bearer <token>" http://127.0.0.1:8000/api/items/
Output:
Authenticated users access protected endpoints with JWT.
Explanation:
djangorestframework-simplejwt
- Implements JWT-based authentication.IsAuthenticated
- Restricts endpoints to authenticated users.
2.4 Filtering and Pagination
Example: Paginated and Filtered API
# myproject/settings.py
REST_FRAMEWORK = {
...
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}
# myapp/views.py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Item
from .serializers import ItemSerializer
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name']
# Install django-filter
pip install django-filter
# myproject/settings.py
INSTALLED_APPS = [
...
'django_filters',
]
# Test filtering and pagination
curl http://127.0.0.1:8000/api/items/?name=Item1
curl http://127.0.0.1:8000/api/items/?page=2
Output:
{
"count": 15,
"next": "http://127.0.0.1:8000/api/items/?page=2",
"previous": null,
"results": [
{"id": 1, "name": "Item1", "description": "Desc", "created_at": "2025-05-16T17:00:00Z"},
...
]
}
Explanation:
DjangoFilterBackend
- Enables filtering by fields likename
.PageNumberPagination
- Limits results per page.
2.5 Consuming API in a Frontend
Example: React Frontend with Django API
# Create React app
npx create-react-app frontend
cd frontend
npm install axios
// frontend/src/App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
function App() {
const [items, setItems] = useState([]);
const [name, setName] = useState('');
useEffect(() => {
axios.get('http://127.0.0.1:8000/api/items/')
.then(response => setItems(response.data))
.catch(error => console.error('Error:', error));
}, []);
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.post('http://127.0.0.1:8000/api/items/', { name });
setName('');
const response = await axios.get('http://127.0.0.1:8000/api/items/');
setItems(response.data);
} catch (error) {
console.error('Error:', error);
}
};
return (
<div className="App">
<h1>Items</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter item name"
required
/>
<button type="submit">Add Item</button>
</form>
<ul style="padding: 0px 0px 0px 20px; margin-top: 0px;">
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
export default App;
/* frontend/src/App.css */
.App {
text-align: center;
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
input {
padding: 8px;
margin-right: 10px;
}
button {
padding: 8px 16px;
}
ul {
list-style: none;
padding: 0;
}
li {
padding: 10px;
border-bottom: 1px solid #ddd;
}
# Run React app
npm start
Output:
React app at http://localhost:3000 displays and creates items via Django API
Explanation:
axios
- Makes HTTP requests to Django API.- React state updates UI dynamically based on API responses.
2.6 Incorrect API Configuration
Example: Missing CORS Setup
# myproject/settings.py (Incorrect)
INSTALLED_APPS = [
...
# 'corsheaders' missing
]
MIDDLEWARE = [
# 'corsheaders.middleware.CorsMiddleware' missing
'django.middleware.common.CommonMiddleware',
...
]
# CORS_ALLOWED_ORIGINS missing
Output:
CORS policy error: No 'Access-Control-Allow-Origin' header
Explanation:
- Missing CORS configuration blocks frontend API requests.
- Solution: Install
django-cors-headers
and setCORS_ALLOWED_ORIGINS
.
03. Effective Usage
3.1 Recommended Practices
- Use viewsets and routers for scalable API development.
Example: Throttling API Requests
# myproject/settings.py
REST_FRAMEWORK = {
...
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day',
},
}
# myapp/views.py
from rest_framework import viewsets
from .models import Item
from .serializers import ItemSerializer
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
throttle_classes = [AnonRateThrottle, UserRateThrottle]
Output:
API limits requests to 100/day for anonymous users, 1000/day for authenticated users
- Implement authentication to secure endpoints.
- Use environment variables for sensitive settings like API keys.
3.2 Practices to Avoid
- Avoid exposing sensitive data in API responses.
Example: Exposing Sensitive Fields
# myapp/serializers.py (Incorrect)
from rest_framework import serializers
from .models import Item
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = '__all__' # Exposes all fields, including sensitive ones
Output:
API returns sensitive fields, risking data exposure
- Solution: Explicitly list safe fields in
fields
.
04. Common Use Cases
4.1 Building a Task Management API
Create a task management API for a productivity app.
Example: Task Management API
# myapp/models.py
from django.db import models
from django.contrib.auth.models import User
class Task(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
# myapp/serializers.py
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'user', 'title', 'completed', 'created_at']
# myapp/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Task
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import TaskViewSet
router = DefaultRouter()
router.register(r'tasks', TaskViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
// frontend/src/App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
function App() {
const [tasks, setTasks] = useState([]);
const [title, setTitle] = useState('');
const token = localStorage.getItem('token');
useEffect(() => {
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
axios.get('http://127.0.0.1:8000/api/tasks/')
.then(response => setTasks(response.data))
.catch(error => console.error('Error:', error));
}
}, [token]);
const handleSubmit = async (e) => {
e.preventDefault();
try {
await axios.post('http://127.0.0.1:8000/api/tasks/', { title, completed: false, user: 1 });
setTitle('');
const response = await axios.get('http://127.0.0.1:8000/api/tasks/');
setTasks(response.data);
} catch (error) {
console.error('Error:', error);
}
};
return (
<div className="App">
<h1>Tasks</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter task"
required
/>
<button type="submit">Add Task</button>
</form>
<ul style="padding: 0px 0px 0px 20px; margin-top: 0px;">
{tasks.map(task => (
<li key={task.id}>{task.title} {task.completed ? '(Completed)' : ''}</li>
))}
</ul>
</div>
);
}
export default App;
Output:
Task management API with user-specific tasks and React frontend
Explanation:
- API restricts tasks to authenticated user’s data.
- React frontend provides interactive task management.
4.2 Building an E-commerce Product API
Create an API for product listings in an e-commerce platform.
Example: E-commerce Product API
# myapp/models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
# myapp/serializers.py
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'name', 'price', 'stock', 'created_at']
# myapp/views.py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Product
from .serializers import ProductSerializer
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'price']
# myapp/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSet
router = DefaultRouter()
router.register(r'products', ProductViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
// frontend/src/App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';
function App() {
const [products, setProducts] = useState([]);
useEffect(() => {
axios.get('http://127.0.0.1:8000/api/products/')
.then(response => setProducts(response.data))
.catch(error => console.error('Error:', error));
}, []);
return (
<div className="App">
<h1>Products</h1>
<div className="products">
{products.map(product => (
<div key={product.id} className="product">
<h3>{product.name}</h3>
<p>${product.price}</p>
<p>Stock: {product.stock}</p>
</div>
))}
</div>
</div>
);
}
export default App;
/* frontend/src/App.css */
.App {
text-align: center;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.products {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.product {
border: 1px solid #ddd;
padding: 10px;
width: 200px;
}
Output:
E-commerce product API with filtering and React frontend
Explanation:
- API supports filtering by name and price.
- React frontend displays product listings dynamically.
Conclusion
Combining Django with REST APIs using Django REST Framework creates robust, scalable backends for modern applications. By mastering serializers, viewsets, authentication, and filtering, developers can build APIs for diverse clients. Key takeaways:
- Use DRF for rapid API development with viewsets and routers.
- Secure APIs with JWT or other authentication methods.
- Implement filtering and pagination for efficient data handling.
- Avoid exposing sensitive data or skipping CORS configuration.
With Django and REST APIs, you can build flexible, API-driven applications that power dynamic user experiences!
Comments
Post a Comment