@@ 1,27 1,29 @@
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.
"""
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.
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.
"""
Class name is pronounced verbally in a deep tone.
'''
def __init__(self, object_list, per_page, allow_empty_first_page=True, link_template='/page/%d/'):
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)
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."
"""
Validates the given 1-based page number.
"""
try:
number = int(number)
except ValueError:
@@ 31,7 33,9 @@ class InfinitePaginator(Paginator):
return number
def page(self, number):
"Returns a Page object for the given 1-based page 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
@@ 45,12 49,16 @@ class InfinitePaginator(Paginator):
return InfinitePage(page_items, number, self)
def _get_count(self):
"Returns the total number of objects, across all pages."
"""
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."
"""
Returns the total number of pages.
"""
raise NotImplementedError
num_pages = property(_get_num_pages)
@@ 62,15 70,19 @@ class InfinitePaginator(Paginator):
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."
"""
Checks for one more item than last on this page.
"""
try:
next_item = self.paginator.object_list[self.number * self.paginator.per_page]
next_item = self.paginator.object_list[
self.number * self.paginator.per_page]
except IndexError:
return False
return True
@@ 80,9 92,10 @@ class InfinitePage(Page):
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)
return ((self.number - 1) * self.paginator.per_page +
len(self.object_list))
'''Bonus methods for creating links'''
#Bonus methods for creating links
def next_link(self):
if self.has_next():
@@ 95,24 108,27 @@ class InfinitePage(Page):
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)
"""
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):
@@ 126,7 142,9 @@ class FinitePaginator(InfinitePaginator):
return number
def page(self, number):
"Returns a Page object for the given 1-based page 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]
@@ 135,7 153,9 @@ class FinitePaginator(InfinitePaginator):
class FinitePage(InfinitePage):
def has_next(self):
"Checks for one more item than last on this page."
"""
Checks for one more item than last on this page.
"""
try:
next_item = self.paginator.object_list[self.paginator.per_page]
except IndexError: