Ana Balica

Hi, I'm Ana.

I'm a software developer. I mostly do Python. This blog is about my adventures with code, travel experiences and relevant life events. I try not to take myself too seriously.

Here's what I'm doing now.

Occasionally I give talks.

Please don't take my words for granted, because Internet is full of bad advice, and I might be part of it inadvertently.

Pagination in Django

Isn’t it delightful when things just work? That’s how it is for Django pagination feature.

Say you are writing a blog in Django. Actually seems like a silly idea. If you want a blog, get yourself a static blog, or tumblr, or wordpress, or blogger (is it still popular?). Ok, maybe it’s not a blog. Probably you want to display a list of books in a paginated way. Definitely not cool to list all 2k books on a single page.

I like Django class-based views. They do for you all the hard work if you stick to the basics. So let’s start writing a CBV. Assume the model contains any kind of gibberish.

# my_library/library/views.py
from django.views.generic import ListView

from library.models import Book


class BookListView(ListView):
    model = Book
    paginate_by = 10
    template_name = 'book_list.html'

This is basically what we ought to do to enable pagination. Just specify how many objects we want on one page. Let’s take a look at the route and the template.

# my_library/urls.py
from django.conf.urls import patterns, url

from library.views import BookListView

urlpatterns = patterns('',
    url(r'^books/$', BookListView.as_view(), name='book_list'),
)
{# my_library/templates/book_list.html #}
{# omit all the additional HTML markup and leave the bare minimum #}
{% for book in object_list %}
  {{ book.title }}
{% endfor %}

Now if you start your server and access the URL 127.0.0.1:8000/books/ you will get 10 book objects listed on the first page. In order to access the second page you need to enter 127.0.0.1:8000/books/?page=2. But your user isn’t supposed to know that (don’t even think about writing a manual for that and giving it to the end user). That’s why we will create pagination buttons.

Django ListView is so smart that it will add to your context some special objects that will allow you to build those pagination buttons. In fact the pagination we will build is reusable, so we will put in a separate snippet and include to any template that requires pagination.

{# my_library/templates/snippets/pagination.html #}
{% if is_paginated %}
  <nav>
    <ul class="pagination">
      {% if page_obj.has_previous %}
        <li>
          <a href="?page={{ page_obj.previous_page_number }}">
            <span>Previous</span>
          </a>
        </li>
      {% else %}
        <li class="disabled">
          <a href="#">
            <span>Previous</span>
          </a>
        </li>
      {% endif %}

      {% for page in paginator.page_range %}
        <li {% if page == page_obj.number %}class="active"{% endif %}>
          <a href="?page={{ page }}">{{ page }}</a>
        </li>
      {% endfor %}

      {% if page_obj.has_next %}
        <li>
          <a href="?page={{ page_obj.next_page_number }}">
            <span>Next</span>
          </a>
        </li>
      {% else %}
        <li {% if not page_obj.has_next %}class="disabled"{% endif %}>
          <a href="#">
            <span>Next</span>
          </a>
        </li>
      {% endif %}
    </ul>
  </nav>
{% endif %}

Now back to our book_list.html template.

{# my_library/templates/book_list.html #}
{% for book in object_list %}
  {{ book.title }}
{% endfor %}

{% include 'snippets/pagination.html' %}

Dead simple. And this snippet will serve you well for all kinds of list views.