How to use elided pagination in Django and solve too many pages problem

Since Django 3.2 there is a way to handle big pagination with large number of pages.

Published: May 31, 2021

This is another short, sort of a reference post about pagination improvements in Django 3.2. Before we get started, let's go over the motivation.

If you have like 15 pages of paginated stuff, then this isn't needed. But if you have like 100 or more, then the standard pagination looks comical.

Django pagination problem many pages

The standard UX for this is to show smaller range + a couple of pages on the end, to give indication as to how much stuff is there and then have ellipsis or something to signify that what's visible is not all.

And thanks to Django 3.2 and its Paginator class, this functionality comes built-in. This will help you solve the problem of having too many pages to show in your pagination component.

Basic usage

The magic happens in the get_elided_page_range method on the Paginator. The basic usage looks like this:

page = request.GET.get('page', 1)
paginator = Paginator(articles, 40)
page_range = paginator.get_elided_page_range(number=page)

This attempt to get the page parameter from the URL query string, creates Paginator and finally gets the elided range. Basically "incomplete" page range.

Parameters

There are a couple of parameters to customize this. One is on_each_side which defaults to 3 and is the number of pages that will be shown before and after the current page (if this is possible of course). The second one is on_ends and defaults to 2. This is used to control how many pages from the end are shown in the paginator.

Django template

That's all the Python code and we can move to the template. I want to say thanks to Vitor Freitas from SimpleIsBetterThanComplex for his article about pagination in Django from August 2016 which is used as a reference and inspiration for the template below.

 <ul class="pagination justify-content-center flex-wrap mt-2 mb-4">
    {% if page_obj.has_previous %}
        <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">&laquo;</a></li>
    {% else %}
        <li class="disabled page-item"><span class="page-link">&laquo;</span></li>
    {% endif %}
    {% for i in page_range|default_if_none:page_obj.paginator.get_elided_page_range %}
        {% if page_obj.number == i %}
            <li class="active page-item"><span class="page-link">{{ i }} <span class="sr-only">(current)</span></span>
            </li>
        {% else %}
            {% if i == page_obj.paginator.ELLIPSIS %}
                <li class="page-item"><span class="page-link">{{ i }}</span></li>
            {% else %}
                <li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
            {% endif %}
        {% endif %}
    {% endfor %}
    {% if page_obj.has_next %}
        <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">&raquo;</a></li>
    {% else %}
        <li class="disabled page-item"><span class="page-link">&raquo;</span></li>
    {% endif %}
</ul>

This template works with Bootstrap 4. For other version of this framework, you will probably have to do some adjustments.

And the result:

Django elided pagination example 1 Django elided pagination example 2 Django elided pagination example 3

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f

iOS blogger and developer with interest in Python/Django. My recent projects include IndieAppsCatalog and ImpressKit.

iOS blogger and developer with interest in Python/Django. My recent projects include IndieAppsCatalog and ImpressKit.