Pagination in Django is efficiently implemented using the Paginator
class from django.core.paginator
. It helps divide large querysets or lists into smaller, manageable chunks displayed page-by-page. This approach improves performance and enhances the user experience by preventing information overload.
To use pagination, you create a Paginator
object with the queryset and the desired number of items per page. You can then access the appropriate page using the page()
method. The resulting page_obj
object, passed to the template context, offers attributes like has_previous
, has_next
, and number
, enabling easy navigation.
Django's JournalListView
class provides built-in support for pagination, simplifying the implementation.
# journal/views.py
from django.views.generic import ListView
from blog.models import Post
class JournalListView(ListView):
model = Journal # 1
template_name = "journal.html" # 2
context_object_name = "entries" # 3
queryset = Journal.journal_published.all() # 4
paginate_by = 12 # 5
Explanation:
Model: Specifies the Journal
model as the data source.
Context Variable: Renames the default object_list
to entires
.
Pagination: Automatically paginates the results with 12 posts per page.
Template: Uses the journal.html
template.
To handle pagination exceptions explicitly in a CBV, override the paginate_queryset
method:
# journal/views.py
from django.core.paginator import EmptyPage, PageNotAnInteger
from django.views.generic import ListView
from blog.models import Post
class CustomJournalListView(ListView):
model = Journal
paginate_by = 12
template_name = "journal.html"
def paginate_queryset(self, queryset, page_size):
paginator = self.get_paginator(queryset, page_size)
page_number = self.request.GET.get('page')
try:
page = paginator.page(page_number)
except PageNotAnInteger:
page = paginator.page(1)
except EmptyPage:
page = paginator.page(paginator.num_pages)
return paginator, page, page.object_list, page.has_other_pages()
Explanation:
PageNotAnInteger
: If the provided page is invalid, display the first page.
EmptyPage
: If the page number is too large, display the last available page.
Create the pagination.html
template. The following template allows users to navigate between pages.
<!-- core/templates/pagination.html -->
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">« |</a>
<a href="?page={{ page_obj.previous_page_number }}">Previous </a>
{% endif %}
<span class="current">
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 }}">| »</a>
{% endif %}
</span>
</div>
Explanation:
Displays "Previous" and "Next" buttons depending on the availability of pages.
Shows the current page number and the total number of pages.
To display the paginated `entries` and navigation links, include the pagination.html
template in your main list template.
<!-- journal/templates/journal.html -->
{% extends 'base.html' %}
{% load i18n %}
{% block content %}
<!-- ======= Journal Section ======= -->
<section class="journal portfolio section-show">
<div class="container">
<div class="section-title">
<h2>{% trans "Journal" %}</h2>
</div>
<div class="row">
<div class="col-lg-12 d-flex justify-content-center">
<ul id="page-filters">
<li data-filter="*" class="filter-active">{% trans "All" %}</li>
{% for entry in filter_entries %}
<li data-filter=".filter-{{ entry.1 }}">{{ entry.0 }}</li>
{% endfor %}
</ul>
</div>
</div>
<div class="row page-container">
{% for entry in entries %}
<div class="col-lg-3 col-md-5 mt-4 portfolio-item filter-{{ entry.category.slug }}">
<div class="icon-box">
<h4><a href="{{ entry.get_absolute_url }}">{{ entry.title}}</a></h4>
<p>{{ entry.name }}</p>
</div>
</div>
{% endfor %}
</div>
<!-- Include pagination -->
{% include "pagination.html" %}
</div>
</section><!-- End Journal Section -->
{% endblock %}
With these steps, you have a complete pagination system implemented in Django, supporting both function-based and class-based views with robust error handling and user-friendly navigation.