Kay provides some classes that help manage paginated data. Paginated data is data that is spread across several pages and is navigated using “Previous” and “Next” links.
These classes are located in the kay.utils.paginator module.
>>> from kay.utils.paginator import Paginator
>>> object_list = ["spam", "eggs", "googoo", "gaagaa"]
>>> p = Paginator(object_list, 2)
>>> p.count
4
>>> p.num_pages
2
>>> p.page_range
[1, 2]
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['spam', 'eggs']
>>> page2 = p.page(2)
>>> page2.object_list
['googoo', 'gaagaa']
>>> page2.has_next
False
>>> page2.has_previous
True
>>> page2.has_other_pages
True
>>> page2.next_page_number
3
>>> page2.previous_page_number
1
>>> page2.start_index
3
>>> page2.end_index
4
>>> p.page(0)
Traceback (most recent call last)
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last)
...
EmptyPage: That page contains no results
ノート
Note that you can give Paginator a list/tuple, a Kay QuerySet, or any other object with a count() or __len__() method. When determining the number of objects contained in the passed object, Paginator will first try calling count(), then fallback to using len() if the passed object has no count() method. This allows objects such as Kay’s QuerySet to use a more efficient count() method when available.
Here’s a slightly more complex example using Paginator in a view to paginate a datastore query. We give both the view and the accompanying jinja2 template to show how you can display the results. This example assumes you have a Contacts model that has already been imported.
The view function looks like this:
from kay.utils.paginator import Paginator, InvalidPage, EmptyPage
from kay.utils import render_to_response
def listing(request):
contact_list = Contacts.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page
# Make sure page request is an int. If not, deliver first page.
try:
page = int(request.args.get('page', '1'))
except ValueError:
page = 1
# If page request (9999) is out of range, deliver last page of results.
try:
contacts = paginator.page(page)
except (EmptyPage, InvalidPage):
contacts = paginator.page(paginator.num_pages)
return render_to_response('list.html', {"contacts": contacts})
{% for contact in contacts.object_list %}
{# 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 %}
<span class="current">
Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
<a href="?page={{ contacts.next_page_number }}">next</a>
{% endif %}
</span>
</div>
Paginator objects have the following constructor.
The total number of objects, across all pages.
ノート
When determining the number of objects contained in object_list, Paginator will first try calling object_list.count(). If object_list has no count() method, then Paginator will fallback to using object_list.__len__(). This allows objects, such as Appengine’s Query, to use a more efficient count() method when available.
The page() method raises InvalidPage if the requested page is invalid (i.e., not an integer) or contains no objects. Generally, it’s enough to trap the InvalidPage exception, but if you’d like more granularity, you can trap either of the following exceptions:
Both of the exceptions are subclasses of InvalidPage, so you can handle them both with a simple except InvalidPage.
You usually won’t construct Pages by hand – you’ll get them using Paginator.page().