Custom Pagination Classes in Django Rest Framework
Last updated 1 month, 1 week ago | 115 views 75 5

Pagination helps keep API responses fast, small, and user-friendly. While DRF provides built-in classes like PageNumberPagination
, LimitOffsetPagination
, and CursorPagination
, there are times when you need custom behavior—and that’s where Custom Pagination Classes come in.
This article covers:
-
Why you may need a custom pagination class
-
How pagination works in DRF
-
How to create your own pagination class
-
Full example implementation
-
Tips and common pitfalls
Why Create a Custom Pagination Class?
You might need custom pagination when:
-
You want a different response format
-
You need to add additional metadata
-
You want to paginate based on unusual rules
-
You need to support multiple strategies (e.g., dynamic
PageNumber
andLimitOffset
) -
You want to include extra links or stats in the response
⚙️ How DRF Pagination Works
All pagination classes in DRF must inherit from BasePagination
or a subclass of it (PageNumberPagination
, LimitOffsetPagination
, etc.). You customize how:
-
The queryset is sliced
-
The response is formatted
-
The parameters are handled
Step-by-Step: Creating a Custom Pagination Class
Let’s walk through creating a custom pagination class that:
-
Uses page numbers
-
Allows dynamic page size via query param
-
Returns a customized JSON response
Step 1: Create the Custom Pagination Class
# pagination.py
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
class MyCustomPagination(PageNumberPagination):
page_size = 5 # default page size
page_size_query_param = 'size' # client can override with ?size=
max_page_size = 50
def get_paginated_response(self, data):
return Response({
'status': 'success',
'total_items': self.page.paginator.count,
'total_pages': self.page.paginator.num_pages,
'current_page': self.page.number,
'next': self.get_next_link(),
'previous': self.get_previous_link(),
'results': data
})
Step 2: Use It in Your View
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
from .pagination import MyCustomPagination
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
pagination_class = MyCustomPagination
Step 3: Try It in Your API
Example request:
GET /api/posts/?page=2&size=10
Example response:
{
"status": "success",
"total_items": 132,
"total_pages": 14,
"current_page": 2,
"next": "http://localhost:8000/api/posts/?page=3&size=10",
"previous": "http://localhost:8000/api/posts/?page=1&size=10",
"results": [
{
"id": 11,
"title": "Post Title",
"content": "Post body..."
},
...
]
}
✅ Full Example (All Files)
models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content']
views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
from .pagination import MyCustomPagination
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
pagination_class = MyCustomPagination
urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register('posts', PostViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
Tips & Best Practices
Tip | Why It Helps |
---|---|
✅ Override get_paginated_response() |
To control the structure of the JSON response |
✅ Set page_size_query_param |
Allows clients to control how much data they get |
✅ Enforce max_page_size |
Prevents abuse (e.g., ?size=10000 ) |
✅ Add metadata like total pages or timestamps | Useful for clients building UIs |
⚠️ Common Pitfalls
Pitfall | Solution |
---|---|
❌ Not returning Response() in get_paginated_response |
Always wrap your response in Response() |
❌ Using the wrong pagination class | Make sure the class inherits from the correct base (PageNumberPagination , etc.) |
❌ Forgetting to set pagination_class in view |
DRF won't paginate unless the view is configured correctly |
❌ Clients sending excessive size values |
Use max_page_size to restrict this |
Conclusion
Custom pagination classes in Django Rest Framework give you the flexibility to design APIs that meet your frontend and performance needs. Whether you're building a RESTful app, infinite scroll UI, or data-heavy dashboard, fine-tuning pagination can lead to better UX and API performance.