How to Build Custom Pagination in Django to Overcome Django Paginator Drawbacks

by Yaseen Dar

Recently, we ran into a situation where page loading was taking us more than 10  seconds. After investigating we found that Django Paginator was the culprit. Being django developers, we are all aware how django built-in paginator works.

I am copying the code from djangoproject.com.  https://docs.djangoproject.com/en/1.9/topics/pagination/#using-paginator-in-a-view

This example assumes, you have a Contacts model that has already been imported.

The view function will look like this:

 

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

from django.shortcuts import render

def listing(request):

    contact_list = Contacts.objects.all()

    paginator = Paginator(contact_list, 25) # Show 25 contacts per page

    page = request.GET.get('page')

    try:

        contacts = paginator.page(page)

    except PageNotAnInteger:

        # If page is not an integer, deliver first page.

       contacts = paginator.page(1)

    except EmptyPage:

    # If page is out of range (e.g. 9999), deliver last page of results.

    contacts = paginator.page(paginator.num_pages)

    return render(request, 'list.html', {'contacts': contacts})

In the template list.html, you will have to include navigation between pages along with any interesting information from the objects themselves: 
{% for contact in contacts %}

    {# Each "contact" is a Contact model object. #}

    {{ contact.full_name|upper }}<br />

    ...

{% endfor %}

 <div class="pagination">

    <span class="step-links">

        {% if contacts.has_previous %}

            <a href="?page={{ contacts.previous_page_number }}">previous</a>

        {% endif %}

        <span class="current">

            Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.

        </span>

        {% if contacts.has_next %}

            <a href="?page={{ contacts.next_page_number }}">next</a>

        {% endif %}

    </span>

</div>

 

Having seen the above code, can you discern what the main drawback of this code is?

The main drawback of django Paginator is that you have to feed whole object list to Paginator,  for which pagination needs to be done, contact_list in the above case. The problem becomes very severe if the Contact table contains huge amounts of entries and we have to do many operations with each contact object before rendering.

If we assume Contact object contains 1 million entries, we have to feed all those entries to Django Paginator. If we then have to do some operation, we have to loop over through 1 million entries. This drastically increases page loading time.

To overcome the above drawbacks,  let us see how to build our own listing function which does pagination quite easily and effectively.

 

def listing(request):

    current_page = request.GET.get('page' ,'1')

    limit = 25 * current_page

    offset = limit - 25

    contact_list = Contacts.objects.all()[offset:limit]  # limiting contacts based on current_page

    total_contacts = Contacts.objects.all().count()  

    total_pages = total_contacts / 25

    if total_contacts % 25 != 0:

        total_pages += 1 # adding one more page if the last page will contains less contacts 

        pagination = make_pagination_html(current_page, total_pages)

    return render(request, 'list.html', {'contacts': contact_list, 'pagination': pagination_string})

 

def make_pagination_html(current_page, total_pages):

    pagination_string = ""

    if current_page > 1:

        pagination_string += '<a href="?page=%s">previous</a>' % (current_page -1)

    pagination_string += '<span class="current"> Page %s of %s </span>' %(current_page, total_pages)

    if current_page < total_pages:

        pagination_string += '<a href="?page=%s">next</a>' % (current_page + 1)

    return pagination_string

And our new list.html will be like this

<div class="pagination">

    <span class="step-links">

      {{pagination|safe}}

   </span>

</div>

 Our listing function overcomes the drawbacks of Django Paginator by  limiting the contact_list from 1 million entries to 25. If we have to do further stuff with contact objects, we have to loop only over 25 entries before rendering them on the template and this will load page very quickly.

 We can further modify make_pagination_html function to render direct page links as well.

Django paginator

In order to display pagination like in the pic above, modify make_pagination_html function as below,

 

def make_pagination_html(current_page, total_pages):

    pagination_string = ""

    if current_page > 1:

        pagination_string += '<a href="?page=%s">←</a>' % (current_page -1)

    pagination_string += "<li class='active'><a href='?page=%s' >%s</a></li>"  % (current_page, current_page)

    count = 1

    value = current_page - 1

    while value > 0 and count_limit < 5:

        pagination_string = "<li><a href='?page=%s'>%s</a></li>" % (value, value) + pagination_string

        value -= 1

        count_limit += 1

    value = current_page + 1

    while value < total_pages  and count_limit < 10

        pagination_string =  pagination_string +"<li><a href='?page=%s'>%s</a></li>" % (value, value)

        value += 1

        count_limit +=1

   if current_page < total_pages:

       pagination_string += '<a href="?page=%s">→</a>' % (current_page + 1)

   return pagination_string

To learn more about Web

Contact Us

Leave a Reply

Your email address will not be published. Required fields are marked *

Tools & Practices

Tools and Technologies we use at Applied

Contact us now

Popular Posts