Your Linux Data Center Experts

A Recipe for Pagination in Django

By  Sean Reifschneider Date March 19, 2008

Introduction

The pagination class in Django is fairly low-level. While you can use it to implement pagination in Django, you probably don't need to do that. This article puts together a number of components to easily do pagination in Django.

Pagination Tag Snippet

Over at djangosnippets there is a snippet for a "paginator" tag. This is a good start, but it isn't very smart about handling pagination links with direct links to the first and last page, an ellipsis, and links around the current page. If you want to have pagination links like this, you will need to use my modified version:

So, either get the snippet from the above link at djangosnippets, or use my modified version (required for these examples to fully work).

Save this into the file "templatetags/paginator.py" in the top level of your application directory. You can determine where this would be by looking at the value of "INSTALLED_APPS" in your "settings.py" file, for example I had:

INSTALLED_APPS = (
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.sites',
   'photoblog.photos',
   )

Which meant I needed to write this to "photoblog/photos/templatetags/paginator.py".

Note: If you don't already have a "templatetags" directory, create it with:

mkdir templatetags
touch templatetags/__init__.py

The "__init__.py" file needs to be there so that the directory can be imported as a module.

Hooking Pagination Into Your View

Next you will need to make it so that pagination works in your view. In my case, I'm using a custom query, but otherwise I can use the "object_list" generic view, so my "index" view looks like:

from django.views.generic.list_detail import object_list

def index(request, format):
   from django.db.models import Q

   photo_list = Photos.objects.filter(
               Q(album = None) | Q(isalbumleader = True)
               ).order_by('-uploadedon')

   return object_list(request, template_name = 'index.html',
         queryset = photo_list, paginate_by = 25)

Your Site-Wide Paginator Template

This tag relies on a site-wide template for what the pagination renders to. This is handy if you have pagination on many different pages, they will all have the same style. So, in your "templates" directory you will need a file called "paginator.html", here is an example of mine:

<div class="pager">
   {% if has_previous %}
      <span class="page">
      <a href="?page={{ previous }}">&lt; Prev</a>
      </span>
   {% endif %}

   {% if show_first %}
      <span class="page"><a href="?page=1">1</a></span>
      <span class="ellipsis">...</span>
   {% endif %}
   {% for linkpage in page_numbers %}
      {% ifequal linkpage page %}
         <span class="current">{{ page }}</span>
      {% else %}
         <span class="page"><a href="?page={{ linkpage }}"
               >{{ linkpage }}</a></span>
      {% endifequal %}
   {% endfor %}
   {% if show_last %}
      <span class="ellipsis">...</span>
      <span class="page"><a href="?page=last">{{ pages }}</a></span>
   {% endif %}
   {% if has_next %}
      <span class="page"><a href="?page={{ next }}">Next &gt;</a></span>
   {% endif %}
</div>

This can basically be used un-modified.

Call Paginator Tag From Template

The last item to do is to put the paginator tag into your template (in my case, this is the "templates/index.html" file. It should look something like this:

{% if is_paginated %}{% load paginator %}{% paginator 3 %}{% endif %}

The "3" is the number of surrounding pages around the current page to show.

Note: In your template, the object_list generic view presents the list of items for the page as "object_list". So a minimal full page would be something like:

{% if is_paginated %}{% load paginator %}{% paginator 3 %}{% endif %}

{% if object_list %}
   <ul>{% for photo in object_list %}
      <li />{{ photo.name }}
   {% endfor %}</ul>
{% endif %}

{% if is_paginated %}{% load paginator %}{% paginator 3 %}{% endif %}

This example includes pagers both above and below the paged data, and a simple list of the photo names.

Sample CSS

The CSS I'm using to style the above is:

<style type="text/css">
   .pager {
      padding-top: 20px;
      padding-left: 40px;
      }
   .pager .page a {
      border: 3px solid #bbbbbb;
      margin-left: 1px;
      margin-right: 1px;
      padding-left: 4px;
      padding-right: 4px;
      text-decoration: none;
      color: #000000;
      }
   .pager .current {
      border: 3px solid #444444;
      margin-left: 2px;
      margin-right: 2px;
      padding-left: 2px;
      }
</style>

Final Notes

A few final things to remember:

Shameless Plug

tummy.com has smart people who can bring a diverse set of knowledge to augment your Linux system administration and managed hosting needs. See the menu on the upper left of this page for more information about our services.

comments powered by Disqus