What We Are Building
In this tutorial you will build a fully working REST API using Django and Django REST Framework (DRF). By the end you will have API endpoints that can:
- List all items
- Get a single item
- Create a new item
- Update an item
- Delete an item
This is called a CRUD API — Create, Read, Update, Delete. It is the foundation of almost every web application.
What You Need
- Python 3.10 or higher installed
- Basic knowledge of Python
- A terminal / command prompt
No Django experience needed — we start from zero.
Step 1 — Set Up Your Project
Create a folder and set up a virtual environment:
mkdir django-api
cd django-api
# Create virtual environment
python -m venv venv
# Activate it (Mac/Linux)
source venv/bin/activate
# Activate it (Windows)
venv\Scripts\activate
Install Django and Django REST Framework:
pip install django djangorestframework
Create a new Django project and app:
django-admin startproject myproject .
python manage.py startapp api
Your folder structure should look like this:
django-api/
├── myproject/
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── api/
│ ├── models.py
│ ├── views.py
│ └── urls.py
└── manage.py
Step 2 — Configure Settings
Open myproject/settings.py and add rest_framework and api 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', # Add this
'api', # Add this
]
Step 3 — Create the Model
We will build a simple Task API. Open api/models.py:
from django.db import models
class Task(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
completed = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Meta:
ordering = ['-created_at']
Run migrations to create the database table:
python manage.py makemigrations
python manage.py migrate
Step 4 — Create the Serializer
A serializer converts your model data to JSON (and back). Create a new file api/serializers.py:
from rest_framework import serializers
from .models import Task
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'description', 'completed', 'created_at']
read_only_fields = ['id', 'created_at']
That is all you need. DRF handles the conversion automatically.
Step 5 — Create the Views
Open api/views.py and write your API views:
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Task
from .serializers import TaskSerializer
@api_view(['GET', 'POST'])
def task_list(request):
"""List all tasks or create a new task."""
if request.method == 'GET':
tasks = Task.objects.all()
serializer = TaskSerializer(tasks, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = TaskSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@api_view(['GET', 'PUT', 'DELETE'])
def task_detail(request, pk):
"""Get, update, or delete a single task."""
try:
task = Task.objects.get(pk=pk)
except Task.DoesNotExist:
return Response({'error': 'Task not found'}, status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = TaskSerializer(task)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = TaskSerializer(task, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
task.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Step 6 — Set Up URLs
Create api/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('tasks/', views.task_list, name='task-list'),
path('tasks/<int:pk>/', views.task_detail, name='task-detail'),
]
Now connect it to the main project URLs. Open myproject/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
Step 7 — Test Your API
Start the development server:
python manage.py runserver
Your API is now live at http://127.0.0.1:8000/api/
Test with curl:
# Get all tasks
curl http://127.0.0.1:8000/api/tasks/
# Create a new task
curl -X POST http://127.0.0.1:8000/api/tasks/ \
-H "Content-Type: application/json" \
-d '{"title": "Learn Django", "description": "Build a REST API"}'
# Get a single task
curl http://127.0.0.1:8000/api/tasks/1/
# Update a task
curl -X PUT http://127.0.0.1:8000/api/tasks/1/ \
-H "Content-Type: application/json" \
-d '{"title": "Learn Django", "description": "Done!", "completed": true}'
# Delete a task
curl -X DELETE http://127.0.0.1:8000/api/tasks/1/
DRF also gives you a browsable API — open http://127.0.0.1:8000/api/tasks/ in your browser and you will see a nice UI to test your endpoints.
Step 8 — Use ViewSets (Cleaner Code)
The function-based views above are easy to understand but there is a cleaner way using ViewSets. Here is the same API in fewer lines:
# api/views.py
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer
class TaskViewSet(viewsets.ModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
# api/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('', include(router.urls)),
]
This gives you all 5 endpoints automatically — list, create, retrieve, update, delete — with just 3 lines in the view.
What's Next
Now that you have a working REST API, here are the next steps to make it production ready:
- Add authentication — use
rest_framework.authentication.TokenAuthenticationso only logged-in users can access the API - Add filtering — use
django-filterto let clients filter tasks bycompleted=true - Add pagination — add
PAGE_SIZE = 10to your DRF settings so large lists are paginated - Deploy to Render — push to GitHub, connect to Render, and your API is live for free
# settings.py — add this for pagination and authentication
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
Django REST Framework is one of the most powerful tools in a Python developer's toolkit. Every web app needs an API and DRF makes it fast and clean to build one.