4

Floating Page Numbers in Django

Posted (Updated ) in Uncategorized

If you’re paginating your data and have tens or hundreds of pages, chances are you’d prefer only a subset of these be displayed in your page number list (example – see page list at bottom). Django-paginator gets 90% of your work done for you however it doesn’t support this one very useful feature. To make a developers life easier, meet flynsarmy_paginator!

flynsarmy_pagination
flynsarmy_pagination in action

 

Download it here.

This app is a simple subclass of django’s built in Paginator and Page classes. The only changes I’ve made are to add an optional adjacent_pages parameter to FlynsarmyPaginator and a .page_range_data variable to the FlynsarmyPage object with the following contents:

  • page_range – same as Paginator.page_range – a list of page numbers but only showing a subset of pages if adjacent_pages was specified in the FlynsarmyPaginator constructor.
  • show_first – Boolean value set to True if ‘1’ isn’t present in the page_range list above.
  • show_last – Boolean vlaue set to True if the last page isn’t present in the page_range list above.

Usage

Add to your INSTALLED_APPS in settings.py:

1
2
3
4
5
6
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    ...,
    'flynsarmy_paginator',
)

An example view (taken mostly from the docs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django.core.paginator import EmptyPage, PageNotAnIntegerfrom flynsarmy_paginator.paginator import FlynsarmyPaginator 
def listing(request):
    contact_list = Contacts.objects.all()
    paginator = Paginator(contact_list, 25, adjacent_objects=6) # Show 25 contacts per page 
    page = request.GET.get('page')
    try:
        contacts = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        contacts = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        contacts = paginator.page(paginator.num_pages)
 
    return render_to_response('list.html', {"contacts": contacts})

and template:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{% for contact in contacts %}
	{# Each "contact" is a Contact model object. #}
	{{ contact.full_name|upper }}<br />
	...
{% endfor %}
 
<div class="pagination">
	<span class="step-links">
		{% if contacts.has_previous %}
			<a href="?page={{ contacts.previous_page_number }}">previous</a>
		{% endif %}
 
		{% if contacts.page_range_data.show_first %}			<a href="?page=1">1</a>			<span class="ellipsis">...</span>		{% endif %}		{% for i in contacts.page_range_data.page_range %}			{% ifequal i contacts.number %}				{{ i }}			{% else %}				<a href="?page={{ i }}">{{ i }}</a>			{% endifequal %}		{% endfor %}		{% if contacts.page_range_data.show_last %}			<span class="ellipsis">...</span>			<a href="?page={{ contacts.paginator.num_pages }}">{{ contacts.paginator.num_pages }}</a>		{% endif %} 
		{% if contacts.has_next %}
			<a href="?page={{ contacts.next_page_number }}">next</a>
		{% endif %}
	</span>
</div>

I’d like to give a big thanks to the people of tummy.com for writing most of the logic behind the .page_range_data.page_range function so I didn’t have to!