Skip to main content

Django: Combining Django with REST APIs

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 like name.
  • 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 set CORS_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