While working on a modern web application quite often you will need to paginate the app be it for better user experience or performance. Fortunately, Django comes with built-in pagination classes for managing paginating data of your application.
In this article, we will go through the pagination process with class-based views and function based views in Django.
Prerequisite
For the sake of this tutorial I am using a blog application - Github repo
The above project is made on Python 3.7, Django 2.1 and Bootstrap 4.3. This is a very basic blog application displaying a list of posts on the homepage but when the number of posts increases we need to split them up.
Recommended Article: Building A Blog Application With Django
Adding Pagination Using Class-Based-Views [ ListView ]
The Django ListView
class comes with built-in support for pagination so all we need to do is take advantage of it. Pagination is controlled by the GET
parameter that controls which page to show.
First, open the views.py
file of your app.
from django.views import generic
from .models import Post
class PostList(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'index.html'
class PostDetail(generic.DetailView):
model = Post
template_name = 'post_detail.html'
Now in the PostList
view we will introduce a new attribute paginate_by
which takes an integer specifying how many objects should be displayed per page. If this is given, the view will paginate objects with paginate_by
objects per page. The view will expect either a page query string parameter (via request.GET
) or a page
variable specified in the URLconf.
class PostList(generic.ListView):
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'index.html'
paginate_by = 3
Now our posts are paginated by 3 posts a page.
Next, to see the pagination in action, we need to edit the template which for this application is the index.html
file paste the below snippet.
{% if is_paginated %}
<nav aria-label="Page navigation conatiner"></nav>
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li><a href="?page={{ page_obj.previous_page_number }}"
class="page-link">« PREV </a></li>
{% endif %} {% if page_obj.has_next %}
<li><a href="?page={{ page_obj.next_page_number }}"
class="page-link"> NEXT »</a></li>
{% endif %}
</ul>
</nav></div>
{% endif %}
Note that we are using Bootstrap 4.3
for this project, if you are using any other frontend framework you may change the classes.
Now run the server and visit http://127.0.0.1:8000/
you should see the page navigation buttons below the posts.
Adding Pagination Using Function-Based-Views
Equivalent function-based view for the above PosList
class would be.
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
def PostList(request):
object_list = Post.objects.filter(status=1).order_by('-created_on')
paginator = Paginator(object_list, 3)
# 3 posts in each page
page = request.GET.get('page')
try:
post_list = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
post_list = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
post_list = paginator.page(paginator.num_pages)
return render(request,
'index.html',
{'page': page,
'post_list': post_list})
So in the view, we instantiate the Paginator class with the number of objects to be displayed on each page i.e 3. Then we have the request.GET.get('page')
parameter which returns the current page number. The page()
method is used to obtain the objects from the desired page number. Below that we have two exception statements for PageNotAnInteger
and EmptyPage
both are subclasses of InvalidPage
finally at the end we are rendering out the HTML file.
Now in your templates paste the below snippet.
{% if post_list.has_other_pages %}
<nav aria-label="Page navigation conatiner"></nav>
<ul class="pagination justify-content-center">
{% if post_list.has_previous %}
<li><a href="?page={{ post_list.previous_page_number }}"
class="page-link">« PREV </a></li>
{% endif %} {% if post_list.has_next %}
<li><a href="?page={{ post_list.next_page_number }}"
class="page-link"> NEXT »</a></li>
{% endif %}
</ul>
</nav></div>
{% endif %}
Save the files and run the server you should see the NEXT button below the post list.