Welcome back to our Django REST Framework (DRF) tutorial series! We set up Django and DRF in Part 1. Part 2 added CRUD for tasks, Part 3 secured it with token authentication, and Part 4 personalized it with user-owned tasks. In Part 5, we’re optimizing our Task Manager API with filtering, pagination, and search—making it user-friendly for large datasets.
By the end, your API will let users filter tasks by status, paginate results, and search by title, all tested with Postman. Ready to level up?
Let’s dive in!
Table of Contents
- Step 1: Install and Configure django-filter
- Step 2: Add Filtering to Views
- Step 3: Implement Pagination
- Step 4: Add Search Functionality
- Step 5: Test with Postman
- Conclusion
- What’s Next?
Step 1: Install and Configure django-filter
We’ll use django-filter
to add filtering capabilities to our API.
Install django-filter
Run this command to install the package:
pip install django-filter
Update taskmanager/settings.py
Add django_filters
to INSTALLED_APPS
and configure DRF to use it:
INSTALLED_APPS = [
...
'django.contrib.auth',
'django.contrib.contenttypes',
'rest_framework',
'rest_framework.authtoken',
'tasks',
'django_filters', # New
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}
-
django_filters
enables filtering by adding query parameters (e.g., ?completed=true). -
DEFAULT_FILTER_BACKENDS
tells DRF to use DjangoFilterBackend globally.
Step 2: Add Filtering to Views
Let’s allow users to filter tasks by completed status, respecting user ownership.
Update tasks/views.py
Modify the TaskListCreateView to include filtering:
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from django_filters.rest_framework import DjangoFilterBackend
from .models import Task
from .serializers import TaskSerializer
class TaskListCreateView(generics.ListCreateAPIView):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
filter_backends = [DjangoFilterBackend] # Enable filtering
filterset_fields = ['completed'] # Allow filtering by completed status
def get_queryset(self):
# Only show tasks created by the authenticated user
return Task.objects.filter(created_by=self.request.user)
def perform_create(self, serializer):
# Automatically set the creator when a task is created
serializer.save(created_by=self.request.user)
class TaskDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
def get_queryset(self):
# Only allow access to tasks created by the authenticated user
return Task.objects.filter(created_by=self.request.user)
What changed?
- Added
filter_backends = [DjangoFilterBackend]
to enable filtering. - Set
filterset_fields = ['completed']
to allow filtering by the completed field.
Step 3: Implement Pagination
Let’s add pagination to limit the number of tasks returned per request.
Update taskmanager/settings.py
Configure DRF’s default pagination:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10, # Number of items per page
}
-
PageNumberPagination
adds a page and page_size query parameters (e.g., ?page=2&page_size=5). -
PAGE_SIZE
sets the default number of items per page (overridable by clients).
Note: No changes are needed in views.py—pagination is handled automatically
Step 4: Add Search Functionality
Let’s enable searching tasks by title.
Update tasks/views.py
Add search to TaskListCreateView
from rest_framework import generics
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from django_filters.rest_framework import DjangoFilterBackend
from .models import Task
from .serializers import TaskSerializer
class TaskListCreateView(generics.ListCreateAPIView):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
filter_backends = [DjangoFilterBackend] # Filtering
filterset_fields = ['completed'] # Filtering by completed
search_fields = ['title'] # Enable search by title
def get_queryset(self):
# Only show tasks created by the authenticated user
return Task.objects.filter(created_by=self.request.user)
def perform_create(self, serializer):
# Automatically set the creator when a task is created
serializer.save(created_by=self.request.user)
class TaskDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = TaskSerializer
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
def get_queryset(self):
# Only allow access to tasks created by the authenticated user
return Task.objects.filter(created_by=self.request.user)
Added search_fields = ['title']
to enable searching by the title field using the ?search= query parameter.
Step 5: Test with Postman
Start your server:
python manage.py runserver
We’ll use Postman to test filtering, pagination, and search.
1. Register a User and Add Multiple Tasks
Register User:
- Method: POST
- URL: http://127.0.0.1:8000/api/register/
- Body: raw > JSON:
{
"username": "newUser1",
"password": "pass123"
}
Send. Copy the token (e.g., bc54e0e61096a8c79d2c5c6d958b2d3ed5fda519).
Log In:
- POST to http://127.0.0.1:8000/api/login/, Body:
{
"username": "newUser1",
"password": "pass123"
}
Expect {"token": "bc54e0e61096a8c79d2c5c6d958b2d3ed5fda519"}.
Add Tasks:
- Method: POST
- URL: http://127.0.0.1:8000/api/tasks/
- Headers: Authorization: Token token1
- Body (repeat with different titles):
{"title": "Task 1", "description": "First task", "completed": false}
{"title": "Task 2", "description": "Second task", "completed": true}
{"title": "Task 3", "description": "Third task", "completed": false}
Send each. Expect 201 Created
2. Test Filtering
Filter Completed Tasks:
- Method: GET
- URL: http://127.0.0.1:8000/api/tasks/?completed=true
- Headers: Token token1
Send. Expect 200 OK with [{"title": "Task 2", ...}].
Filter Uncompleted Tasks:
Send. Expect 200 OK with [{"title": "Task 1", ...}, {"title": "Task 3", ...}].
3. Test Pagination
Default Pagination:
- Method: GET
- URL: http://127.0.0.1:8000/api/tasks/
- Headers: Token token1
Send. Expect 200 OK with up to 10 tasks (e.g., {"count": 3, "next": null, "previous": null, "results": [...]}).
Custom Page Size:
Send. Expect 200 OK with 2 tasks per page (e.g., first page with Task 1 and Task 2).
4. Test Search
Search by Title:
- Method: GET
- URL: http://127.0.0.1:8000/api/tasks/?search=Task
- Headers: Token token1
Send. Expect 200 OK with all tasks containing "Task" in the title (e.g., all three tasks).
Specific Search:
URL: http://127.0.0.1:8000/api/tasks/?search=Task 1
Send. Expect 200 OK with [{"title": "Task 1", ...}].
5. Combine Features
Filter and Paginate:
Send. Expect 200 OK with one uncompleted task (e.g., Task 1).
Conclusion
🎉 Great job! Your Task Manager API now handles filtering by completed status, paginates results for scalability, and searches by title—making it efficient and user-friendly. These optimizations build on the user-owned tasks from Part 4, keeping your API secure and user-friendly.
Summary Checklist
- ✅Installed and configured django-filter
- ✅Added filtering to views
- ✅Implemented pagination
- ✅Added search functionality
- ✅Tested with Postman
What’s Next?
Part 6 covers API security best practices. Stay tuned!
Top comments (0)