It expands your outline into a detailed professional post suitable for blogs, documentation, or tutorials.
It includes text, code, and structured explanations only — no icons, emojis, or non-text content.
Introduction
When learning Django REST Framework (DRF), the best way to truly understand how everything fits together is to build a complete API project. So far, you might have worked with serializers, viewsets, and routers individually. In this post, we’ll combine all these components into a small, fully functional RESTful API.
Our project will implement an API for managing books. Each book will have a title, an author (a user), and a publication date. You’ll learn how to integrate authentication, filtering, searching, and ordering — all powered by Django REST Framework.
By the end of this post, you’ll have a complete, production-ready API that supports:
- CRUD (Create, Read, Update, Delete) operations
- Token-based authentication
- Filtering, searching, and ordering
- Pagination and permissions
Let’s begin step by step.
Step 1: Setting Up the Django Project
First, create a new Django project. Make sure you have Python and Django installed.
pip install django djangorestframework django-filter
django-admin startproject library_project
cd library_project
python manage.py startapp books
Once the app is created, add both rest_framework and books to your INSTALLED_APPS in library_project/settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'django_filters',
'books',
]
We’ve also added rest_framework.authtoken and django_filters for authentication and filtering support.
Now let’s define our model.
Step 2: Creating the Book Model
Open books/models.py and create the Book model.
from django.db import models
from django.contrib.auth.models import User
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(User, on_delete=models.CASCADE)
published_date = models.DateField()
def __str__(self):
return self.title
This model defines a simple data structure:
- title: a short text field for the book’s title
- author: a relationship to the built-in Django
Usermodel - published_date: a
DateFieldto record when the book was published
Then run migrations:
python manage.py makemigrations
python manage.py migrate
Step 3: Creating a Serializer
A serializer converts the Book model into JSON format and validates incoming data when creating or updating records.
Create books/serializers.py:
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
Using ModelSerializer allows DRF to automatically generate fields and handle validation based on the model definition. You can customize validation later if needed.
Step 4: Creating the ViewSet
A ViewSet in Django REST Framework handles all CRUD operations in a single class. Instead of writing multiple views, DRF provides a unified way to manage Create, Read, Update, and Delete actions.
Open books/views.py and add the following:
from rest_framework import viewsets, permissions, filters
from django_filters.rest_framework import DjangoFilterBackend
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['author']
search_fields = ['title']
ordering_fields = ['published_date']
Explanation
- queryset defines the default set of data available for the API.
- serializer_class specifies how to convert model instances to JSON and back.
- permission_classes ensures only authenticated users can access the endpoints.
- filter_backends enables built-in filtering and searching.
- filterset_fields allows users to filter by the author field.
- search_fields adds text search on the book title.
- ordering_fields enables sorting results by publication date.
With this configuration, your API automatically supports querying, filtering, and ordering without writing custom logic.
Step 5: Registering the ViewSet with a Router
Routers in DRF automatically create URL patterns for your ViewSets.
Create a new file named books/urls.py:
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = router.urls
Then include this in your project’s main urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('books.urls')),
path('api-auth/', include('rest_framework.urls')),
]
Now run the development server:
python manage.py runserver
Visithttp://127.0.0.1:8000/api/books/
You’ll see the Django REST Framework’s interactive API browser.
Step 6: Testing Basic CRUD Operations
Before adding authentication, let’s test basic CRUD operations.
You can use Postman, cURL, or simply the DRF browser interface.
Create a Book (POST)
Endpoint: /api/books/
Example Request:
{
"title": "Learning Django",
"author": 1,
"published_date": "2024-05-20"
}
Example Response:
{
"id": 1,
"title": "Learning Django",
"author": 1,
"published_date": "2024-05-20"
}
Read Books (GET)
Endpoint: /api/books/
Returns a list of all books.
Retrieve Single Book
Endpoint: /api/books/1/
Update a Book (PUT or PATCH)
Endpoint: /api/books/1/
Example Request:
{
"title": "Learning Django REST Framework",
"author": 1,
"published_date": "2024-06-01"
}
Delete a Book (DELETE)
Endpoint: /api/books/1/
This removes the record from the database.
At this point, all CRUD operations are functional.
Step 7: Enabling Token Authentication
Next, we’ll secure our API using Token Authentication. This ensures that only registered users can access the data.
Update Settings
In settings.py, configure the default authentication classes:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
Create Tokens for Users
Add the following to your app’s urls.py:
from rest_framework.authtoken import views
from django.urls import path
urlpatterns += [
path('token/', views.obtain_auth_token),
]
Now you can request a token by sending a POST request to /api/token/ with valid user credentials.
Example Request:
{
"username": "john",
"password": "password123"
}
Example Response:
{
"token": "ab23b3e9d5fa28f7c431b12c93e7b27d31af21e2"
}
Use this token in subsequent API calls:
Authorization: Token ab23b3e9d5fa28f7c431b12c93e7b27d31af21e2
Now only authenticated users can perform CRUD operations.
Step 8: Filtering, Searching, and Ordering
Our BookViewSet already includes filtering and searching capabilities. Let’s explore how they work.
Filtering
You can filter books by author using a query parameter:
/api/books/?author=1
This returns only books written by the user with ID 1.
Searching
Use the search parameter to look for titles:
/api/books/?search=Django
DRF performs a case-insensitive search on the title field.
Ordering
You can sort results by the published date:
/api/books/?ordering=published_date
To reverse the order:
/api/books/?ordering=-published_date
With just a few lines in your ViewSet, your API now supports advanced querying features.
Step 9: Pagination
Large datasets should be paginated to avoid overwhelming users or the server.
Enable pagination in settings.py:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
Now when listing books, responses will include pagination metadata:
{
"count": 25,
"next": "http://127.0.0.1:8000/api/books/?page=2",
"previous": null,
"results": [
{
"id": 1,
"title": "Learning Django",
"author": 1,
"published_date": "2024-05-20"
}
]
}
Step 10: Testing with the Django Admin
Django’s admin panel is automatically available for managing books and users.
Register the Model
In books/admin.py:
from django.contrib import admin
from .models import Book
admin.site.register(Book)
Run the server, visit /admin/, and log in with your superuser credentials.
You can create and edit books manually, which is helpful for testing the API.
Step 11: Implementing Custom Behavior in ViewSet
You can easily override default behaviors. For instance, automatically associate the current user as the book author:
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_fields = ['author']
search_fields = ['title']
ordering_fields = ['published_date']
def perform_create(self, serializer):
serializer.save(author=self.request.user)
Now, when a user creates a book, the author field will automatically be set to the authenticated user.
Step 12: Handling Permissions More Precisely
Sometimes you want to restrict editing or deleting to the user who created the record.
You can use custom permissions.
Create books/permissions.py:
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
Update BookViewSet to use it:
from .permissions import IsAuthorOrReadOnly
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsAuthorOrReadOnly]
Now users can view all books but can edit or delete only their own.
Step 13: Extending the API with Custom Endpoints
You can add extra actions beyond the default CRUD operations using the @action decorator.
Example:
from rest_framework.decorators import action
from rest_framework.response import Response
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
@action(detail=False, methods=['get'])
def recent(self, request):
recent_books = Book.objects.order_by('-published_date')[:5]
serializer = self.get_serializer(recent_books, many=True)
return Response(serializer.data)
Now the endpoint /api/books/recent/ returns the five most recently published books.
Step 14: Project Directory Recap
Your folder structure should now look like this:
library_project/
│
├── library_project/
│ ├── settings.py
│ ├── urls.py
│
├── books/
│ ├── models.py
│ ├── serializers.py
│ ├── views.py
│ ├── urls.py
│ ├── admin.py
│ ├── permissions.py
│
└── manage.py
This modular design separates concerns clearly — models handle data, serializers handle conversion, views handle logic, and routers handle routing.
Step 15: Testing the Complete API Workflow
Let’s verify the entire flow end-to-end:
- Register a user via the Django admin or Django’s authentication views.
- Obtain a token using
/api/token/. - Use the token in your HTTP Authorization header.
- Create a book with
POST /api/books/. - List all books with
GET /api/books/. - Search, filter, and order results with query parameters.
- Update or delete your own books only.
This workflow covers every essential operation in a secure and RESTful way.
Step 16: Advanced Enhancements
Once you’ve mastered the basics, here are ways to expand the API:
- Pagination customization: Implement cursor or limit-offset pagination for large datasets.
- JWT authentication: Replace token authentication with JSON Web Tokens using
djangorestframework-simplejwt. - Throttling and rate limiting: Control how often users can access endpoints.
- Documentation: Generate API documentation using tools like
drf-yasgordrf-spectacular. - Testing: Write unit tests for serializers, views, and models using Django’s test framework.
Step 17: Understanding the DRF Workflow
Here’s a high-level overview of what happens when a request hits your API:
- URL Router: Directs the request to the appropriate ViewSet method (list, create, retrieve, update, destroy).
- ViewSet: Uses the queryset and serializer to process the request.
- Serializer: Validates and serializes data for input/output.
- Permissions: Check if the user is allowed to perform the action.
- Response: Returns serialized JSON data or error messages.
This request-response cycle ensures data integrity, validation, and security throughout.
Step 18: Why DRF Is Ideal for API Development
Django REST Framework provides several advantages:
- Rapid development: Build full CRUD APIs with minimal code.
- Built-in security: Permissions, authentication, and throttling are already integrated.
- Powerful serialization: Automatic model serialization with flexible customization.
- Browsable API: Test endpoints interactively during development.
- Scalability: Supports large applications through modular design.
Whether you’re developing internal tools or production-grade services, DRF offers the right balance between simplicity and power.
Step 19: Troubleshooting Common Issues
- 401 Unauthorized:
Ensure you’re passing the token in the header usingAuthorization: Token your_token_here. - 403 Forbidden:
Check your permissions; the user may not be allowed to modify another author’s book. - Field Validation Errors:
Ensure all required fields are included in POST or PUT requests. - Filter or Search Not Working:
Verify thatdjango-filteris installed and listed inINSTALLED_APPS. - Incorrect URLs:
Make sure your app’s URLs are included in the project’s mainurls.py.
These small checks often resolve 90% of development issues.
Leave a Reply