From 5619f8ca2aa666af41c43988467e6e83d11e345e Mon Sep 17 00:00:00 2001 From: leahculver Date: Wed, 11 Feb 2009 16:44:50 -0800 Subject: [PATCH] silly FinitePaginator for finite lists of items --- pagination/paginator.py | 58 ++++++++++++++++++++++++++++++++++++++++- pagination/tests.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/pagination/paginator.py b/pagination/paginator.py index 56e0a31..ae92486 100644 --- a/pagination/paginator.py +++ b/pagination/paginator.py @@ -92,4 +92,60 @@ class InfinitePage(Page): def previous_link(self): if self.has_previous(): return self.paginator.link_template % (self.number - 1) - return None \ No newline at end of file + 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 \ No newline at end of file diff --git a/pagination/tests.py b/pagination/tests.py index c0a4a6c..fadb870 100644 --- a/pagination/tests.py +++ b/pagination/tests.py @@ -62,6 +62,7 @@ u'[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]' # Testing InfinitePaginator >>> from paginator import InfinitePaginator + >>> InfinitePaginator >>> p = InfinitePaginator(range(20), 2, link_template='/bacon/page/%d') @@ -86,4 +87,45 @@ False '/bacon/page/4' >>> p3.previous_link() '/bacon/page/2' + +# Testing FinitePaginator + +>>> from paginator import FinitePaginator + +>>> FinitePaginator + +>>> p = FinitePaginator(range(20), 2, offset=10, link_template='/bacon/page/%d') +>>> p.validate_number(2) +2 +>>> p.orphans +0 +>>> p3 = p.page(3) +>>> p3 + +>>> p3.start_index() +10 +>>> p3.end_index() +6 +>>> p3.has_next() +True +>>> p3.has_previous() +True +>>> p3.next_link() +'/bacon/page/4' +>>> p3.previous_link() +'/bacon/page/2' + +>>> p = FinitePaginator(range(20), 20, offset=10, link_template='/bacon/page/%d') +>>> p2 = p.page(2) +>>> p2 + +>>> p2.has_next() +False +>>> p3.has_previous() +True +>>> p2.next_link() + +>>> p2.previous_link() +'/bacon/page/1' + """ \ No newline at end of file -- 2.20.1