A Recipe for Pagination in Django

By Sean Reifschneider Date March 19, 2008

Introduction

Pagination Tag Snippet

The pagination class in Django is fairly low-level. While youuse 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.

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:



3 pages around the current page.

But no ellipsis if we are towards the beginning or end, or if the ellipsis would only cover for one page. And hide next/previous if at the beginning or end.

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".

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

mkdir templatetags touch templatetags/__init__.py

Hooking Pagination Into Your View

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

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 }}">< 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 ></a></span> {% endif %} </div>

Call Paginator Tag From Template

This can basically be used un-modified.

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.

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 %}

Sample CSS

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

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



The number of objects per page is specified in the view in the call to object_list(), via the "paginate_by" argument. You will need to plug your own query into the view.

Shameless Plug

A few final things to remember:

Please enable JavaScript to view the comments powered by Disqus.

Disqus

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.