Table of Contents
- Introduction
- Prerequisites
- What is Django REST Framework?
- Understanding REST API Principles
- Setting Up Your Development Environment
- Creating Your Django Project
- Building Your First REST API
- Implementing Authentication
- Adding Custom Permissions
- Testing Your API
- Project Structure
- Conclusion
Introduction
Building REST APIs with Django has never been easier thanks to Django REST Framework (DRF). This comprehensive tutorial will guide you through creating a complete RESTful API for a to-do list application from scratch.
By the end of this guide, you'll have a fully functional Django REST API that includes:
- CRUD operations for managing tasks
- JWT authentication system
- Custom permissions for user-specific data
- Professional project structure
- Best practices for API development
Whether you're new to Django or looking to add API capabilities to your existing applications, this tutorial provides everything you need to get started with Django REST Framework.
Prerequisites
Before diving into this Django REST Framework tutorial, ensure you have:
- Python 3.8+ installed on your system
- Basic understanding of Python programming
- Familiarity with Django fundamentals
- Text editor or IDE (VS Code, PyCharm, etc.)
- Command line/terminal access
What is Django REST Framework?
Django REST Framework (DRF) is a powerful toolkit for building Web APIs in Django. Built on top of Django, DRF provides:
Key Features of Django REST Framework
- Serialization: Convert Django models to JSON/XML and vice versa
- Authentication: Multiple authentication schemes (JWT, Token, Session)
- Permissions: Fine-grained access control
- Browsable API: Interactive web interface for testing
- ViewSets: Simplified view logic for CRUD operations
- Routers: Automatic URL routing
Why Choose Django REST Framework?
DRF is the industry standard for building APIs with Django because it:
- Reduces development time significantly
- Provides robust security features
- Offers excellent documentation
- Has a large, active community
- Integrates seamlessly with Django
Understanding REST API Principles
What Makes an API RESTful?
REST (Representational State Transfer) APIs follow specific architectural constraints:
- Stateless: Each request contains all necessary information
- Resource-based: URLs represent resources, not actions
- HTTP Methods: Use standard HTTP verbs (GET, POST, PUT, DELETE)
- JSON Format: Consistent data format for requests/responses
HTTP Methods in REST APIs
Method | Purpose | Example |
---|---|---|
GET | Retrieve data | GET /api/tasks/ - Get all tasks |
POST | Create new resource | POST /api/tasks/ - Create new task |
PUT | Update entire resource | PUT /api/tasks/1/ - Update task 1 |
PATCH | Partial update | PATCH /api/tasks/1/ - Update specific fields |
DELETE | Remove resource | DELETE /api/tasks/1/ - Delete task 1 |
RESTful URL Structure
For our to-do API, we'll use these endpoints:
/api/tasks/ # List all tasks, create new task
/api/tasks/{id}/ # Retrieve, update, or delete specific task
/api/auth/login/ # User authentication
/api/auth/refresh/ # Token refresh
🚀 Interactive HTTP Methods Demo
Client
Server
Setting Up Your Development Environment
Step 1: Install Required Packages
Create a new directory for your project and set up a virtual environment:
# Create project directory
mkdir django-rest-api-tutorial
cd django-rest-api-tutorial
# Create virtual environment
python -m venv venv
# Activate virtual environment
# On Windows:
venv\Scripts\activate
# On macOS/Linux:
source venv/bin/activate
# Install Django and Django REST Framework
pip install django djangorestframework
pip install djangorestframework-simplejwt # For JWT authentication
Step 2: Verify Installation
Confirm your installation by checking versions:
python -c "import django; print(django.get_version())"
python -c "import rest_framework; print('DRF installed successfully')"
Creating Your Django Project
Step 1: Initialize Django Project
django-admin startproject todoapi
cd todoapi
Step 2: Create Django App
python manage.py startapp tasks
Step 3: Configure Settings
Edit todoapi/settings.py
to include DRF and your app:
# todoapi/settings.py
import os
from pathlib import Path
from datetime import timedelta
BASE_DIR = Path(__file__).resolve().parent.parent
# Add your apps and DRF to INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_simplejwt',
'tasks', # Your app
]
# REST Framework configuration
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 20,
}
# JWT Settings
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS': True,
}
Building Your First REST API
Step 1: Create the Task Model
Create your data model in tasks/models.py
:
# tasks/models.py
from django.db import models
from django.contrib.auth.models import User
class Task(models.Model):
PRIORITY_CHOICES = [
('low', 'Low'),
('medium', 'Medium'),
('high', 'High'),
]
title = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
completed = models.BooleanField(default=False)
priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default='medium')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='tasks')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
due_date = models.DateTimeField(blank=True, null=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
Step 2: Create Database Migrations
python manage.py makemigrations
python manage.py migrate
Step 3: Build the Serializer
Create tasks/serializers.py
:
# tasks/serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
class Meta:
model = Task
fields = [
'id', 'title', 'description', 'completed',
'priority', 'user', 'created_at', 'updated_at', 'due_date'
]
read_only_fields = ['id', 'user', 'created_at', 'updated_at']
def validate_title(self, value):
if len(value.strip()) < 3:
raise serializers.ValidationError("Title must be at least 3 characters long.")
return value
class UserSerializer(serializers.ModelSerializer):
tasks_count = serializers.SerializerMethodField()
class Meta:
model = User
fields = ['id', 'username', 'email', 'tasks_count']
def get_tasks_count(self, obj):
return obj.tasks.count()
Step 4: Create API Views
Build your views in tasks/views.py
:
# tasks/views.py
from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from .models import Task
from .serializers import TaskSerializer
from .permissions import IsOwnerOrReadOnly
class TaskViewSet(viewsets.ModelViewSet):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['completed', 'priority']
search_fields = ['title', 'description']
ordering_fields = ['created_at', 'updated_at', 'due_date']
ordering = ['-created_at']
def get_queryset(self):
"""Return tasks for the current user only"""
return Task.objects.filter(user=self.request.user)
def perform_create(self, serializer):
"""Set the user when creating a new task"""
serializer.save(user=self.request.user)
@action(detail=False, methods=['get'])
def completed(self, request):
"""Get all completed tasks"""
completed_tasks = self.get_queryset().filter(completed=True)
serializer = self.get_serializer(completed_tasks, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def pending(self, request):
"""Get all pending tasks"""
pending_tasks = self.get_queryset().filter(completed=False)
serializer = self.get_serializer(pending_tasks, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def mark_complete(self, request, pk=None):
"""Mark a task as completed"""
task = self.get_object()
task.completed = True
task.save()
serializer = self.get_serializer(task)
return Response(serializer.data)
Step 5: Configure URLs
Create tasks/urls.py
:
# tasks/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, basename='tasks')
urlpatterns = [
path('api/', include(router.urls)),
]
Update main todoapi/urls.py
:
# todoapi/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('tasks.urls')),
path('api/auth/login/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
Implementing Authentication
Step 1: Create Superuser
python manage.py createsuperuser
Step 2: Test Authentication
Start your development server:
python manage.py runserver
Obtain JWT tokens by making a POST request to http://127.0.0.1:8000/api/auth/login/
:
{
"username": "your_username",
"password": "your_password"
}
You'll receive:
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
}
Step 3: Use Access Token
Include the access token in your API requests:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Adding Custom Permissions
Create tasks/permissions.py
:
# tasks/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the task.
return obj.user == request.user
Testing Your API
Manual Testing with cURL
- Get access token:
curl -X POST http://127.0.0.1:8000/api/auth/login/ \
-H "Content-Type: application/json" \
-d '{"username": "your_username", "password": "your_password"}'
- Create a task:
curl -X POST http://127.0.0.1:8000/api/tasks/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Learn Django REST Framework", "priority": "high"}'
- List all tasks:
curl -X GET http://127.0.0.1:8000/api/tasks/ \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Using Django REST Framework Browsable API
Navigate to http://127.0.0.1:8000/api/tasks/
in your browser to access the interactive API interface.
Project Structure
After completing this tutorial, your project structure will look like:
todoapi/
├── manage.py
├── todoapi/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py
│ └── asgi.py
└── tasks/
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── serializers.py
├── views.py
├── permissions.py
├── urls.py
├── tests.py
└── migrations/
├── __init__.py
└── 0001_initial.py
File Descriptions
models.py
: Contains your Task model with all necessary fieldsserializers.py
: Defines how model data is converted to/from JSONviews.py
: Contains your API logic and endpoint handlerspermissions.py
: Custom permission classes for access controlurls.py
: URL routing configuration for your API endpointsmigrations/
: Database migration files for your models
Best Practices and Tips
1. API Versioning
Consider implementing API versioning for future updates:
# In urls.py
urlpatterns = [
path('api/v1/', include('tasks.urls')),
]
2. Error Handling
Implement proper error handling in your views:
from rest_framework.exceptions import ValidationError
def perform_create(self, serializer):
if Task.objects.filter(user=self.request.user, title=serializer.validated_data['title']).exists():
raise ValidationError("Task with this title already exists.")
serializer.save(user=self.request.user)
3. API Documentation
Add documentation using tools like drf-spectacular
:
pip install drf-spectacular
4. Testing
Write comprehensive tests for your API endpoints:
# tests.py
from django.test import TestCase
from rest_framework.test import APIClient
from django.contrib.auth.models import User
class TaskAPITestCase(TestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(username='testuser', password='testpass')
def test_create_task(self):
self.client.force_authenticate(user=self.user)
response = self.client.post('/api/tasks/', {'title': 'Test Task'})
self.assertEqual(response.status_code, 201)
Conclusion
Congratulations! You've successfully built a complete REST API using Django REST Framework. This tutorial covered:
- Setting up Django and DRF from scratch
- Creating models, serializers, and views
- Implementing JWT authentication
- Adding custom permissions
- Following REST API best practices
- Testing your API endpoints
Next Steps
To further enhance your Django REST API skills, consider:
- Adding more complex relationships between models
- Implementing file upload functionality
- Adding real-time features with Django Channels
- Deploying your API to production (Heroku, AWS, DigitalOcean)
- Creating a frontend to consume your API (React, Vue.js, Angular)
- Adding comprehensive testing and CI/CD pipelines
Additional Resources
Building REST APIs with Django REST Framework opens up countless possibilities for creating scalable, maintainable web services. Whether you're building a simple CRUD API or a complex system with multiple integrations, DRF provides the tools and flexibility you need to succeed.
Start building your next API project today and join the thousands of developers who trust Django REST Framework for their web service needs!
Comments