When user-generated content becomes big, it's best practice to paginate returning content. Pagination is used to retrieve a large amount of data in chunks.
In this article, you will learn how to customize one of the pagination approach, which is implemented by PageNumberPagination class in Django Rest Framework. We will cover enabling pagination in ListAPIView and APIView.
Let's suppose, we are building a backend for a blog website and it has a model Post
with the following fields.
class Post(models.Model):
title = models.CharField(max_length=256)
slug = models.SlugField(unique=True)
text = models.TextField()
image = models.ImageField(upload_to='blog/%Y-%m/', blank=True, null=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
is_active = models.BooleanField(default=True)
created_dt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Create a module called paginators.py
inside the same application directory with the following content:
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size = 5
page_size_query_param = 'page_size'
max_page_size = 20
page_query_param = 'page'
Our custom paginator class inherits from PageNumberPagination
, so we can customize it as we want.
page_size = 5
- Each page will contain 5 recordspage_query_param = 'page'
means that query param is called “page” which is used to tell the DRF which page we want to read: /api/v1/posts?page=2page_size_query_param = 'page_size'
- also we can control how many records to fetch per page by including query param page_size: /api/v1/posts?page=2&page_size=3. In this case,page_size = 5
will be overridden by query parameter value (?page_size=3). So it returns 3 records on each page. But it does not return records more thanmax_page_size
value, which is 20 in our case.max_page_size = 20
: maximum allowed records count it can return per page.
Enable pagination in generics.ListAPIView
Now, in module views.py
import CustomPagination
class and assign it to generics.ListAPIView
class pagination_class
attribute
from rest_framework import generics
from . import serializers
from .paginators import CustomPagination
from . import models
class PostListView(generics.ListAPIView):
authentication_classes = ()
permission_classes = ()
serializer_class = serializers.BlogSerializer
pagination_class = CustomPagination
queryset = models.Post.objects.all().order_by('-created_dt')
Enable pagination in APIView
class PostListView(APIView):
authentication_classes = ()
permission_classes = ()
serializer_class = serializers.BlogSerializer
pagination_class = CustomPagination
queryset = models.Post.objects.all().order_by('-created_dt')
@property
def paginator(self):
"""The paginator instance associated with the view, or `None`."""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
def paginate_queryset(self, queryset):
"""Return a single page of results, or `None` if pagination is disabled."""
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
"""Return a paginated style `Response` object for the given output data."""
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
def get(self, request):
queryset = self.queryset.filter(is_active=True).order_by('-created_dt')
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.serializer_class(queryset, many=True)
return Response(serializer.data)
Create a module serializers.py
and paste the following code into it:
from rest_framework import serializers
from . import models
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = models.Post
fields = (
'id',
'title',
'text',
'image',
'author',
'is_active',
'created_dt',
)
Then register post list endpoint view in blog/urls.py
:
from django.urls import path
from . import views
app_name = 'api_blog'
urlpatterns = [
path('', views.PostListView.as_view(), name='post-list'),
]
In the root urls.py
, include the blog application routes.
urlpatterns = [
path('admin/', admin.site.urls),
...
# API
path('api/v1/posts', include('blog.urls', namespace='api_blog')),
]
Now, you can use any REST API client to test our endpoint. Send a request to http://127.0.0.1:8000/api/v1/posts and http://127.0.0.1:8000/api/v1/posts?page=2