Django Portfolio Journal

Django Pagination: A Comprehensive Guide Introduction


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:

  1. Model: Specifies the Journal model as the data source.

  2. Context Variable: Renames the default object_list to entires.

  3. Pagination: Automatically paginates the results with 12 posts per page.

  4. Template: Uses the journal.html template.

Error Handling for CBVs

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.


Pagination Template

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">&laquo; |</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 }}">| &raquo;</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.


Include the Pagination Template

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.


Designed by BootstrapMade and modified by DoriDoro