Add pagination component to Wagtail

Michael Yin

Last updated on October 07 2021

Table of Contents

Wagtail Tutorial Series:

To get the latest learning resource for Wagtail 4, please check Build Blog With Wagtail CMS (4.0.0)

  1. Create Wagtail Project
  2. Dockerizing Wagtail App
  3. Add Blog Models to Wagtail
  4. How to write Wagtail page template
  5. Add Bootstrap Theme to Wagtail
  6. How to use StreamField in Wagtail
  7. Wagtail Routable Page
  8. Add pagination component to Wagtail
  9. Customize Wagtail Page URL
  10. Add Full Text Search to Wagtail
  11. Add Markdown Support to Wagtail
  12. Add LaTeX Support & Code Highlight In Wagtail
  13. How to Build Form Page in Wagtail
  14. How to Create and Manage Menus in Wagtail
  15. Wagtail SEO Guide
  16. Source code: https://github.com/AccordBox/wagtail-tailwind-blog

Wagtail Tips:

  1. Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
  2. Wagtail Tip #2: How to Export & Restore Wagtail Site

Write style in Wagtail:

  1. How to use SCSS/SASS in your Django project (Python Way)
  2. How to use SCSS/SASS in your Django project (NPM Way)

Other Wagtail Topics:

  1. How to make Wagtail project have good coding style
  2. How to do A/B Testing in Wagtail CMS 
  3. How to build a landing page using Wagtail CMS 
  4. How to support multi-language in Wagtail CMS 

More Wagtail articles and eBooks written by me

Objectives

By the end of this chapter, you should be able to:

  1. Learn how to build pagination with Django Paginator
  2. Handle the querystring with custom Django template tag

Pagination

Let's add pagination to our BlotPage

Update BlogPage.get_context in blog/models.py

from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator

class BlogPage(RoutablePageMixin, Page):

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        context['blog_page'] = self

        # https://docs.djangoproject.com/en/3.1/topics/pagination/#using-paginator-in-a-view-function
        paginator = Paginator(self.posts, 2)
        page = request.GET.get("page")
        try:
            posts = paginator.page(page)
        except PageNotAnInteger:
            posts = paginator.page(1)
        except EmptyPage:
            posts = paginator.object_list.none()

        context["posts"] = posts
        return context

Notes:

  1. We removed the context['posts'] = self.posts
  2. Use Django Paginator to create paginator instance from the self.posts, the number 2 here is the number of item per page.
  3. The page number is from the URl querystring ?page=

Next, let's add add pagination component to the wagtail_bootstrap_blog/templates/blog/blog_page.html

{# Pagination #}
{# https://docs.djangoproject.com/en/3.1/topics/pagination/#paginating-a-listview #}
<ul class="pagination">
  {% if posts.has_previous %}
    <li class="page-item">
      <a href="?page={{ posts.previous_page_number }}" class="page-link">
        Previous
      </a>
    </li>
  {% else %}
    <li class="page-item disabled">
      <a href="#" class="page-link">
        Previous
      </a>
    </li>
  {% endif %}

  {% if posts.has_next %}
    <li class="page-item">
      <a href="?page={{ posts.next_page_number }}" class="page-link">
        Next
      </a>
    </li>
  {% else %}
    <li class="page-item disabled">
      <a href="#" class="page-link">
        Next
      </a>
    </li>
  {% endif %}
</ul>

Notes:

  1. We use ?page={{ posts.previous_page_number }} to append the page querystring to the url to make pagination work.
  2. As for the number of item per page, you can get it from the page field, or even Django global settings.

URL which already has querystring

Let's take a look at this URL http://www.example.com/?q=wagtail

If we use code above to generat the next url, it would be http://www.example.com/?q=wagtail&page=2

This is not correct and we should use a better way to handle the querystring in the URL.

Update blog/templatetags/blogapp_tags.py

from urllib.parse import urlparse, urlunparse
from django.http import QueryDict

@register.simple_tag
def url_replace(request, **kwargs):
    """
    This tag can help us replace or add querystring

    TO replace the page field in URL
    {% url_replace request page=page_num %}
    """
    (scheme, netloc, path, params, query, fragment) = urlparse(request.get_full_path())
    query_dict = QueryDict(query, mutable=True)
    for key, value in kwargs.items():
        query_dict[key] = value
    query = query_dict.urlencode()
    return urlunparse((scheme, netloc, path, params, query, fragment))

Notes:

  1. Here we added a django template tag to help us solve the problem.
  2. It would get the query_dict from the current request url, and then use the kwargs to generate the new url.

Update pagination in wagtail_bootstrap_blog/templates/blog/blog_page.html

<ul class="pagination">
  {% if posts.has_previous %}
    <li class="page-item">
      <a href="{% url_replace request page=posts.previous_page_number %}" class="page-link">
        Previous
      </a>
    </li>
  {% else %}
    <li class="page-item disabled">
      <a href="#" class="page-link">
        Previous
      </a>
    </li>
  {% endif %}

  {% if posts.has_next %}
    <li class="page-item">
      <a href="{% url_replace request page=posts.next_page_number %}" class="page-link">
        Next
      </a>
    </li>
  {% else %}
    <li class="page-item disabled">
      <a href="#" class="page-link">
        Next
      </a>
    </li>
  {% endif %}
</ul>

Notes:

  1. We replaced ?page={{ posts.previous_page_number }} with {% url_replace request page=posts.previous_page_number %}
  2. The url_replace would help us keep existing querystring in the url when adding page querystring.

Wagtail Tutorial Series:

To get the latest learning resource for Wagtail 4, please check Build Blog With Wagtail CMS (4.0.0)

  1. Create Wagtail Project
  2. Dockerizing Wagtail App
  3. Add Blog Models to Wagtail
  4. How to write Wagtail page template
  5. Add Bootstrap Theme to Wagtail
  6. How to use StreamField in Wagtail
  7. Wagtail Routable Page
  8. Add pagination component to Wagtail
  9. Customize Wagtail Page URL
  10. Add Full Text Search to Wagtail
  11. Add Markdown Support to Wagtail
  12. Add LaTeX Support & Code Highlight In Wagtail
  13. How to Build Form Page in Wagtail
  14. How to Create and Manage Menus in Wagtail
  15. Wagtail SEO Guide
  16. Source code: https://github.com/AccordBox/wagtail-tailwind-blog

Wagtail Tips:

  1. Wagtail Tip #1: How to replace ParentalManyToManyField with InlinePanel
  2. Wagtail Tip #2: How to Export & Restore Wagtail Site

Write style in Wagtail:

  1. How to use SCSS/SASS in your Django project (Python Way)
  2. How to use SCSS/SASS in your Django project (NPM Way)

Other Wagtail Topics:

  1. How to make Wagtail project have good coding style
  2. How to do A/B Testing in Wagtail CMS 
  3. How to build a landing page using Wagtail CMS 
  4. How to support multi-language in Wagtail CMS 

More Wagtail articles and eBooks written by me

Launch Products Faster with Django

SaaS Hammer helps you launch products in faster way. It contains all the foundations you need so you can focus on your product.

Michael Yin

Michael is a Full Stack Developer from China who loves writing code, tutorials about Django, and modern frontend tech.

He has published some ebooks on leanpub and tech course on testdriven.io.

He is also the founder of the AccordBox which provides the web development services.

Table of Contents

Django SaaS Template

It aims to save your time and money building your product, developed by Michael Yin

Learn More

This book will teach you how to build a SPA (single-page application) with React and Wagtail CMS

Read More

Subscribe

Get notified about new great Web Development Tutorial