Django Pagination of Data

(Comments)

Pagination is, dividing data into multiple pages each of fixed size. Modern web applications host huge amounts of data and returning all the data at once is not only not user-friendly but it can choke the network.

How to use pagination in Django.

Django's paginator takes a list of objects and size of a page, then divides the list into individual pages and provide various methods to retrieve and manage pages and objects.


The Django official documentation provides the following code example which demonstrate how paginator works.

>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)

>>> p.count
4
>>> p.num_pages
2

Paginators take a list (or a queryset) and page number, then divide the list into pages.

>>> type(p.page_range)  # `<type 'rangeiterator'>` in Python 2.
<class 'range_iterator'>
>>> p.page_range
range(1, 3)

>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']

>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True

A page from paginator also provides methods to check if it is the last pages in the list, or if it the first page.

>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4

>>> p.page(0)
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last):
...
EmptyPage: That page contains no results

How do we use the paginator in the view?

Let us assume we have a model called Movie. Our view will look for a url parameter named page which gives the page number of the page the user wants. Then our view is defined like this:

def view_movies(request):
    all_movies = Movies.objects.all()
    page_number = request.GET.get('page')
    paginator = Paginator(all_movies, 10) # 10 items per page
    movies = paginator.page(page_number)
    return render(request, 'list_movies.html', {'movies': movies})

all_movies object is the queryset that includes all the movies in the database. The paginator takes the queryset and returns a single page object. We pass this page object to the context of the template. Our template will make use of the page as follows:

{% for movie in movies %}
<h3>{{movie.title}}</h3>
{#... use some html to display details ... #}
{% endfor %}

<div>
    {% if movies.has_previous %}
    <a href="?page={{movies.previous_page_number}}">Previous</a> {% else %}
    <span>Previous</span> {% endif %}

    <span>
    Page {{movies.number}} of {{movies.paginator.num_pages}}.
    </span> {% if movies.has_next %}
    <a href="?page={{movies.next_page_number}}">Next</a> {% else %}
    <span>Next</span> {% endif %}
</div>

The paginator class gives us methods to check if there exists a next page, or a previous page. We are using them in the template to display the links to the next and previous pages. We are also using the number() and num_pages() to show the page numbers and the total number of pages.


While our view and template is good enough to show the list, we need to handle possible exceptions. We change our view method to the following:

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

def view_movies(request):
    all_movies = Movies.objects.all()
    page_number = request.GET.get('page')
    paginator = Paginator(all_movies, 10) # 10 items per page
    try:
        movies = paginator.page(page_number)
        # This can raise an error
    except PageNotAnInteger:
        # If the page number is not an integer
        # show the first page
        movies = paginator.page(1)
    except EmptyPage:
        # If the page number is out of range
        # show the last page
        movies = paginator.page(paginator.num_pages)

    return render(request, 'list_movies.html', {'movies': movies})

We handled some of the possible errors while dealing with the pagination.

Comments

Recent Posts

Archive

2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom