9a6ec830bd5853d490051e3ae6b4b91f3da1791b
[django-pagination.git] / pagination / paginator.py
1 from django.core.paginator import Paginator, Page, PageNotAnInteger, EmptyPage
2
3 class InfinitePaginator(Paginator):
4     '''
5         Paginator designed for cases when it's not important to know how many total pages.
6         This is useful for any object_list that has no count() method or can be used to
7         improve performance for MySQL by removing counts.
8         
9         The orphans parameter has been removed for simplicity and there's a link template string
10         for creating the links to the next and previous pages.
11         
12         Class name is pronounced verbally in a deep tone.
13     '''
14
15     def __init__(self, object_list, per_page, allow_empty_first_page=True, link_template='/page/%d/'):
16         orphans = 0 # no orphans
17         super(InfinitePaginator, self).__init__(object_list, per_page, orphans, allow_empty_first_page)
18         # no count or num pages
19         del self._num_pages, self._count
20         # bonus links
21         self.link_template = link_template
22
23     def validate_number(self, number):
24         "Validates the given 1-based page number."
25         try:
26             number = int(number)
27         except ValueError:
28             raise PageNotAnInteger('That page number is not an integer')
29         if number < 1:
30             raise EmptyPage('That page number is less than 1')
31         return number
32
33     def page(self, number):
34         "Returns a Page object for the given 1-based page number."
35         number = self.validate_number(number)
36         bottom = (number - 1) * self.per_page
37         top = bottom + self.per_page
38         page_items = self.object_list[bottom:top]
39         # check moved from validate_number
40         if not page_items:
41             if number == 1 and self.allow_empty_first_page:
42                 pass
43             else:
44                 raise EmptyPage('That page contains no results')
45         return InfinitePage(page_items, number, self)
46
47     def _get_count(self):
48         "Returns the total number of objects, across all pages."
49         raise NotImplementedError
50     count = property(_get_count)
51
52     def _get_num_pages(self):
53         "Returns the total number of pages."
54         raise NotImplementedError
55     num_pages = property(_get_num_pages)
56
57     def _get_page_range(self):
58         """
59         Returns a 1-based range of pages for iterating through within
60         a template for loop.
61         """
62         raise NotImplementedError
63     page_range = property(_get_page_range)
64
65 class InfinitePage(Page):
66
67     def __repr__(self):
68         return '<Page %s>' % self.number
69
70     def has_next(self):
71         "Checks for one more item than last on this page."
72         try:
73             next_item = self.paginator.object_list[self.number * self.paginator.per_page]
74         except IndexError:
75             return False
76         return True
77
78     def end_index(self):
79         """
80         Returns the 1-based index of the last object on this page,
81         relative to total objects found (hits).
82         """
83         return (self.number - 1) * self.paginator.per_page + len(self.object_list)
84     
85     '''Bonus methods for creating links'''
86
87     def next_link(self):
88         if self.has_next():
89             return self.paginator.link_template % (self.number + 1)
90         return None
91
92     def previous_link(self):
93         if self.has_previous():
94             return self.paginator.link_template % (self.number - 1)
95         return None