Pagination is the process of dividing a large amount of data into smaller pages. Instead of displaying hundreds or thousands of records on one page, Django can display a limited number of records at a time.
Pagination is commonly used for student lists, product lists, blog posts, search results, employee records, and admin dashboards.
Paginator class for dividing QuerySets and other data into
pages.
Pagination divides a large collection of records into multiple smaller pages.
For example, if a website has 100 students and displays 10 students per page:
Page 1 → Students 1 - 10
Page 2 → Students 11 - 20
Page 3 → Students 21 - 30
...
Page 10 → Students 91 - 100
Pagination improves the usability of pages containing large datasets.
Django provides the Paginator class through
django.core.paginator.
from django.core.paginator import Paginator
It can divide a QuerySet or another list-like collection into pages.
The basic syntax is:
paginator = Paginator(
object_list,
per_page
)
For example:
paginator = Paginator(
students,
10
)
This means 10 students will be displayed on each page.
Import Paginator before using it.
from django.core.paginator import Paginator
It can then be used inside a Django view.
A QuerySet can be passed directly to Paginator.
students = Student.objects.all()
paginator = Paginator(
students,
10
)
The QuerySet is divided into pages containing up to 10 records each.
Use the page() method to retrieve a particular page.
page_number = 2
page_obj = paginator.page(
page_number
)
The returned object represents page 2.
Usually, the page number is supplied through a query-string parameter.
For example:
/students/?page=2
Read the page number using:
page_number = request.GET.get(
"page"
)
The value returned by request.GET.get() is a string. Django's
Paginator can work with the page number value, but you can also validate or
convert it when needed.
page_number = request.GET.get(
"page",
1
)
Here, page 1 is used when no page parameter is supplied.
from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Student
def student_list(request):
students = Student.objects.all()
paginator = Paginator(
students,
10
)
page_number = request.GET.get(
"page"
)
page_obj = paginator.get_page(
page_number
)
return render(
request,
"students.html",
{
"page_obj": page_obj
}
)
The get_page() method is convenient for handling page numbers
that are invalid or outside the available range.
page_obj = paginator.get_page(
page_number
)
It provides a convenient way to obtain a page object for a requested page.
The page object can be looped over in the template.
{% for student in page_obj %}
<p>
{{ student.name }}
</p>
{% endfor %}
Only the records belonging to the current page are displayed.
The num_pages attribute provides the total number of pages.
{{ page_obj.paginator.num_pages }}
For example, 100 students with 10 students per page produce 10 pages.
Use number to get the current page number.
{{ page_obj.number }}
For example:
Page 3 of 10
can be displayed with:
Page {{ page_obj.number }}
of {{ page_obj.paginator.num_pages }}
Use has_previous to determine whether a previous page exists.
{% if page_obj.has_previous %}
Previous page is available.
{% endif %}
This is useful for displaying a Previous button.
Use has_next to check whether another page exists.
{% if page_obj.has_next %}
Next page is available.
{% endif %}
The previous_page_number method returns the previous page
number.
{{ page_obj.previous_page_number }}
Use it only when has_previous is true.
The next_page_number method returns the next page number.
{{ page_obj.next_page_number }}
Use it only when has_next is true.
{% if page_obj.has_previous %}
<a href="?page={{
page_obj.previous_page_number
}}">
Previous
</a>
{% endif %}
{% if page_obj.has_next %}
<a href="?page={{
page_obj.next_page_number
}}">
Next
</a>
{% endif %}
You can provide direct links to the first and last pages.
<a href="?page=1">
First
</a>
<a href="?page={{
page_obj.paginator.num_pages
}}">
Last
</a>
These links can make navigation easier for large datasets.
The page_range property of the paginator can be used to
iterate over available page numbers.
{% for number in page_obj.paginator.page_range %}
<a href="?page={{ number }}">
{{ number }}
</a>
{% endfor %}
<div>
{% if page_obj.has_previous %}
<a href="?page=1">
First
</a>
<a href="?page={{
page_obj.previous_page_number
}}">
Previous
</a>
{% endif %}
<span>
Page {{ page_obj.number }}
of
{{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{
page_obj.next_page_number
}}">
Next
</a>
<a href="?page={{
page_obj.paginator.num_pages
}}">
Last
</a>
{% endif %}
</div>
Pagination can be combined with filtering.
students = Student.objects.filter(
course="Python"
)
paginator = Paginator(
students,
10
)
Only matching records are divided into pages.
It is usually useful to define a predictable ordering before paginating a QuerySet.
students = Student.objects.all().order_by(
"name"
)
paginator = Paginator(
students,
10
)
This helps keep the page results consistently ordered.
Pagination can also work with search results.
query = request.GET.get(
"q",
""
)
students = Student.objects.filter(
name__icontains=query
).order_by("name")
paginator = Paginator(
students,
10
)
page_obj = paginator.get_page(
request.GET.get("page")
)
The search results can then be displayed page by page.
When search and pagination are combined, pagination links should preserve the search parameter.
<a href="?q={{ query }}&page=2">
Page 2
</a>
Otherwise, clicking a pagination link may remove the current search filter.
Paginator.has_previous before creating a Previous link.has_next before creating a Next link.Get QuerySet
↓
Filter / Search
↓
Order Data
↓
Create Paginator
↓
Read Page Number
↓
Get Page Object
↓
Send Page Object to Template
↓
Display Records
↓
Previous / Next Navigation
views.py
from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Student
def student_list(request):
students = Student.objects.all().order_by(
"name"
)
paginator = Paginator(
students,
10
)
page_number = request.GET.get(
"page"
)
page_obj = paginator.get_page(
page_number
)
return render(
request,
"students.html",
{
"page_obj": page_obj
}
)
students.html
{% for student in page_obj %}
<p>
{{ student.name }}
</p>
{% endfor %}
<div>
{% if page_obj.has_previous %}
<a href="?page={{
page_obj.previous_page_number
}}">
Previous
</a>
{% endif %}
<span>
Page {{ page_obj.number }}
of
{{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{
page_obj.next_page_number
}}">
Next
</a>
{% endif %}
</div>
Django's Paginator makes it easy to divide large datasets
into smaller pages.
Paginator from django.core.paginator.Paginator.get_page() to obtain a page object.page_obj in the template.has_previous and has_next for navigation.number for the current page.num_pages for the total number of pages.Paginator divides large datasets into pages.get_page() retrieves the requested page.page_obj contains the records for the current page.has_previous checks for a previous page.has_next checks for a next page.num_pages gives the total number of pages.Question: Which Django class is used to divide a large QuerySet into multiple pages?