A Recipe for Pagination in Django
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:
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".
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 }}">< 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>
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:
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
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.