My favorites | Sign in
Project Home Downloads Wiki Issues Source
Checkout   Browse   Changes    
 
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from django.core.paginator import Paginator, Page, PageNotAnInteger, EmptyPage

class InfinitePaginator(Paginator):
"""
Paginator designed for cases when it's not important to know how many total
pages. This is useful for any object_list that has no count() method or can
be used to improve performance for MySQL by removing counts.

The orphans parameter has been removed for simplicity and there's a link
template string for creating the links to the next and previous pages.
"""

def __init__(self, object_list, per_page, allow_empty_first_page=True,
link_template='/page/%d/'):
orphans = 0 # no orphans
super(InfinitePaginator, self).__init__(object_list, per_page, orphans,
allow_empty_first_page)
# no count or num pages
del self._num_pages, self._count
# bonus links
self.link_template = link_template

def validate_number(self, number):
"""
Validates the given 1-based page number.
"""
try:
number = int(number)
except ValueError:
raise PageNotAnInteger('That page number is not an integer')
if number < 1:
raise EmptyPage('That page number is less than 1')
return number

def page(self, number):
"""
Returns a Page object for the given 1-based page number.
"""
number = self.validate_number(number)
bottom = (number - 1) * self.per_page
top = bottom + self.per_page
page_items = self.object_list[bottom:top]
# check moved from validate_number
if not page_items:
if number == 1 and self.allow_empty_first_page:
pass
else:
raise EmptyPage('That page contains no results')
return InfinitePage(page_items, number, self)

def _get_count(self):
"""
Returns the total number of objects, across all pages.
"""
raise NotImplementedError
count = property(_get_count)

def _get_num_pages(self):
"""
Returns the total number of pages.
"""
raise NotImplementedError
num_pages = property(_get_num_pages)

def _get_page_range(self):
"""
Returns a 1-based range of pages for iterating through within
a template for loop.
"""
raise NotImplementedError
page_range = property(_get_page_range)


class InfinitePage(Page):

def __repr__(self):
return '<Page %s>' % self.number

def has_next(self):
"""
Checks for one more item than last on this page.
"""
try:
next_item = self.paginator.object_list[
self.number * self.paginator.per_page]
except IndexError:
return False
return True

def end_index(self):
"""
Returns the 1-based index of the last object on this page,
relative to total objects found (hits).
"""
return ((self.number - 1) * self.paginator.per_page +
len(self.object_list))

#Bonus methods for creating links

def next_link(self):
if self.has_next():
return self.paginator.link_template % (self.number + 1)
return None

def previous_link(self):
if self.has_previous():
return self.paginator.link_template % (self.number - 1)
return None

class FinitePaginator(InfinitePaginator):
"""
Paginator for cases when the list of items is already finite.

A good example is a list generated from an API call. This is a subclass
of InfinitePaginator because we have no idea how many items exist in the
full collection.

To accurately determine if the next page exists, a FinitePaginator MUST be
created with an object_list_plus that may contain more items than the
per_page count. Typically, you'll have an object_list_plus with one extra
item (if there's a next page). You'll also need to supply the offset from
the full collection in order to get the page start_index.

This is a very silly class but useful if you love the Django pagination
conventions.
"""

def __init__(self, object_list_plus, per_page, offset=None,
allow_empty_first_page=True, link_template='/page/%d/'):
super(FinitePaginator, self).__init__(object_list_plus, per_page,
allow_empty_first_page, link_template)
self.offset = offset

def validate_number(self, number):
super(FinitePaginator, self).validate_number(number)
# check for an empty list to see if the page exists
if not self.object_list:
if number == 1 and self.allow_empty_first_page:
pass
else:
raise EmptyPage('That page contains no results')
return number

def page(self, number):
"""
Returns a Page object for the given 1-based page number.
"""
number = self.validate_number(number)
# remove the extra item(s) when creating the page
page_items = self.object_list[:self.per_page]
return FinitePage(page_items, number, self)

class FinitePage(InfinitePage):

def has_next(self):
"""
Checks for one more item than last on this page.
"""
try:
next_item = self.paginator.object_list[self.paginator.per_page]
except IndexError:
return False
return True

def start_index(self):
"""
Returns the 1-based index of the first object on this page,
relative to total objects in the paginator.
"""
## TODO should this holler if you haven't defined the offset?
return self.paginator.offset

Change log

r48 by floguy on Mar 29, 2009   Diff
Nitpicky stuff and PEP8ification
Go to: 
Project members, sign in to write a code review

Older revisions

r47 by floguy on Mar 29, 2009   Diff
Merge branch 'master' of
git@github.com:ericflo/django-
pagination
All revisions of this file

File info

Size: 5771 bytes, 171 lines
Powered by Google Project Hosting