3 class InvalidPage(Exception):
6 class PageNotAnInteger(InvalidPage
):
9 class EmptyPage(InvalidPage
):
12 class Paginator(object):
13 def __init__(self
, object_list
, per_page
, orphans
=0, allow_empty_first_page
=True):
14 self
.object_list
= object_list
15 self
.per_page
= per_page
16 self
.orphans
= orphans
17 self
.allow_empty_first_page
= allow_empty_first_page
18 self
._num
_pages
= self
._count
= None
20 def validate_number(self
, number
):
21 "Validates the given 1-based page number."
25 raise PageNotAnInteger('That page number is not an integer')
27 raise EmptyPage('That page number is less than 1')
28 if number
> self
.num_pages
:
29 if number
== 1 and self
.allow_empty_first_page
:
32 raise EmptyPage('That page contains no results')
35 def page(self
, number
):
36 "Returns a Page object for the given 1-based page number."
37 number
= self
.validate_number(number
)
38 bottom
= (number
- 1) * self
.per_page
39 top
= bottom
+ self
.per_page
40 if top
+ self
.orphans
>= self
.count
:
42 return Page(self
.object_list
[bottom
:top
], number
, self
)
45 "Returns the total number of objects, across all pages."
46 if self
._count
is None:
48 self
._count
= self
.object_list
.count()
49 except (AttributeError, TypeError):
50 # AttributeError if object_list has no count() method.
51 # TypeError if object_list.count() requires arguments
52 # (i.e. is of type list).
53 self
._count
= len(self
.object_list
)
55 count
= property(_get_count
)
57 def _get_num_pages(self
):
58 "Returns the total number of pages."
59 if self
._num
_pages
is None:
60 if self
.count
== 0 and not self
.allow_empty_first_page
:
63 hits
= max(1, self
.count
- self
.orphans
)
64 self
._num
_pages
= int(ceil(hits
/ float(self
.per_page
)))
65 return self
._num
_pages
66 num_pages
= property(_get_num_pages
)
68 def _get_page_range(self
):
70 Returns a 1-based range of pages for iterating through within
73 return range(1, self
.num_pages
+ 1)
74 page_range
= property(_get_page_range
)
76 QuerySetPaginator
= Paginator
# For backwards-compatibility.
79 def __init__(self
, object_list
, number
, paginator
):
80 self
.object_list
= object_list
82 self
.paginator
= paginator
85 return '<Page %s of %s>' % (self
.number
, self
.paginator
.num_pages
)
88 return self
.number
< self
.paginator
.num_pages
90 def has_previous(self
):
91 return self
.number
> 1
93 def has_other_pages(self
):
94 return self
.has_previous() or self
.has_next()
96 def next_page_number(self
):
97 return self
.number
+ 1
99 def previous_page_number(self
):
100 return self
.number
- 1
102 def start_index(self
):
104 Returns the 1-based index of the first object on this page,
105 relative to total objects in the paginator.
107 # Special case, return zero if no items.
108 if self
.paginator
.count
== 0:
110 return (self
.paginator
.per_page
* (self
.number
- 1)) + 1
114 Returns the 1-based index of the last object on this page,
115 relative to total objects found (hits).
117 # Special case for the last page because there can be orphans.
118 if self
.number
== self
.paginator
.num_pages
:
119 return self
.paginator
.count
120 return self
.number
* self
.paginator
.per_page