Django Pagination
In this post, you’ll be learning about Django Pagination.
Table of Contents
Setup Project
Note
Read our post on Django Project Installation and Setup
Create App and Migrate Models
Now create a new app and name it filter_and_pagination then make app discoverable by adding it to INSTALLED_APPS
in settings.py.
For our post, we’ll be taking an example of Books and Authors and display them in a table.
I have created two models Book and Author then migrate them to the database.
In models.py:
from django.db import models # Create your models here. class Book(models.Model): BOOK_STATUS=( ('PUBLISHED', 'Published'), ('ON_HOLD', 'On Hold'), ) id = models.AutoField(primary_key=True) book_name = models.CharField(max_length=255) author = models.ForeignKey('Author',on_delete=models.CASCADE,related_name='author') status = models.CharField(max_length=255, choices = BOOK_STATUS) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table="books" verbose_name="Book" verbose_name_plural="Books" def __str__(self): return self.book_name @property def get_status_verbose_name(self): for row in range(0,2): if self.BOOK_STATUS[row][0] == self.status: status_verbose = self.BOOK_STATUS[row][1] break return status_verbose class Author(models.Model): id = models.AutoField(primary_key=True) author_name = models.CharField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "authors" verbose_name = "Author" verbose_name_plural = "Authors" def __str__(self): return self.author_name
Migrate models using Django Migration commands we can see that table books and authors are created in database.
Making views and pagination
In views.py we have list_data
function and we are retrieving all books and sending to template for displaying.
To Paginate books
queryset you have to import Paginator
from django.core.paginator
package.
The Paginator
is a class it takes queryset list and per_page
an integer as arguments then it is assigned to paginator
object. The method paginator.get_page(page)
takes page
as an argument from a request query string request.GET
.
As Django querysets are lazy they are not evaluated when we call Book.objects.all()
. So there won’t be any performance lag for calling .all()
.
from django.shortcuts import render from filter_and_pagination.models import Book, Author from django.core.paginator import Paginator # Create your views here. def list_data(request): books = Book.objects.all() paginator = Paginator(books,3) page = request.GET.get('page') books = paginator.get_page(page) ctx={ "books" : books } path='filter_and_pagination/listing.html' return render(request,path,ctx)
In apps(filter_and_pagination)/urls.py the url listing
maps to list_data
function in views.py file.
app_name = 'filter_and_pagination' urlpatterns = [ path('listing', views.list_data, name='listing'), ]
Make template in path=”filter_and_pagination/templates/filter_and_pagination/filter_listing.html”
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Book Listing</title> <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css"> </head> <body> <div> <div style="border: 1px dashed #333;" > <h5 style="text-align: center;" >Book Filtering</h5> </div> <div> <div class="" > <ul class="pagination"> {% if books.has_previous %} <li><a href="{% url 'filter_and_pagination:listing' %}?page={{books.previous_page_number}}"><span class="glyphicon glyphicon-chevron-left"></span></a></li> {% endif %} {% for num in books.paginator.page_range %} {% if books.number == num %} <li class="active"><a href="{% url 'filter_and_pagination:listing' %}?page={{num}}">{{num}}</a></li> {% elif num > books.number|add:'-3' and num < books.number|add:'3' %} <li><a href="{% url 'filter_and_pagination:listing' %}?{{filter_url}}&page={{num}}">{{num}}</a></li> {% endif %} {% endfor %} {% if books.has_next %} <li><a href="{% url 'filter_and_pagination:listing' %}?page={{books.next_page_number}}"><span class="glyphicon glyphicon-chevron-right"></span></a></li> {% endif %} </ul> </div> </div> <div style="" > <table border="1" style="border-collapse: collapse"> <thead> <tr> <th>Sl no</th> <th>Book Name</th> <th>Author</th> <th>Book Status</th> <th>Total Books Count</th> </tr> </thead> <tbody> {% for book in books %} <tr> <td>{{forloop.counter}}</td> <td>{{book.book_name}}</td> <td>({{book.author.id}}) <strong>{{book.author.author_name}}</strong></td> <td>{{book.get_status_verbose_name}}</td> <td>{{book.author.get_books_count}}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </body> </html>
Note
The books
an object also has paginator properties.
- has_previous: This property returns a boolean value. It returns
True
if the previous page exists. - number: returns the currently active page number.
- paginator.count: returns total number of Objects.
- paginator.num_pages: returns total number of pages.
- paginator.page_range: This property returns a range of total pages. We can display page numbers by looping through this property.
- has_previous: This property returns a boolean value. It returns
True
if the previous page exists. - has_next: returns
True
if next page exists.
Output
Conclusion
So we have come to the conclusion part of our Django Pagination post. If you like this then please share and for queries comment below. We’ll reach to you soon.
Recent Posts
- Django Querysets | Backward, Reverse and ManyToMany Relationships
- Django Querysets | One To One Foreign Key, Inner Joins, Query Filtering




