app/django/core/paginator.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 class InvalidPage(Exception):
       
     2     pass
       
     3 
       
     4 class Paginator(object):
       
     5     def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
       
     6         self.object_list = object_list
       
     7         self.per_page = per_page
       
     8         self.orphans = orphans
       
     9         self.allow_empty_first_page = allow_empty_first_page
       
    10         self._num_pages = self._count = None
       
    11 
       
    12     def validate_number(self, number):
       
    13         "Validates the given 1-based page number."
       
    14         try:
       
    15             number = int(number)
       
    16         except ValueError:
       
    17             raise InvalidPage('That page number is not an integer')
       
    18         if number < 1:
       
    19             raise InvalidPage('That page number is less than 1')
       
    20         if number > self.num_pages:
       
    21             if number == 1 and self.allow_empty_first_page:
       
    22                 pass
       
    23             else:
       
    24                 raise InvalidPage('That page contains no results')
       
    25         return number
       
    26 
       
    27     def page(self, number):
       
    28         "Returns a Page object for the given 1-based page number."
       
    29         number = self.validate_number(number)
       
    30         bottom = (number - 1) * self.per_page
       
    31         top = bottom + self.per_page
       
    32         if top + self.orphans >= self.count:
       
    33             top = self.count
       
    34         return Page(self.object_list[bottom:top], number, self)
       
    35 
       
    36     def _get_count(self):
       
    37         "Returns the total number of objects, across all pages."
       
    38         if self._count is None:
       
    39             self._count = len(self.object_list)
       
    40         return self._count
       
    41     count = property(_get_count)
       
    42 
       
    43     def _get_num_pages(self):
       
    44         "Returns the total number of pages."
       
    45         if self._num_pages is None:
       
    46             hits = self.count - 1 - self.orphans
       
    47             if hits < 1:
       
    48                 hits = 0
       
    49             if hits == 0 and not self.allow_empty_first_page:
       
    50                 self._num_pages = 0
       
    51             else:
       
    52                 self._num_pages = hits // self.per_page + 1
       
    53         return self._num_pages
       
    54     num_pages = property(_get_num_pages)
       
    55 
       
    56     def _get_page_range(self):
       
    57         """
       
    58         Returns a 1-based range of pages for iterating through within
       
    59         a template for loop.
       
    60         """
       
    61         return range(1, self.num_pages + 1)
       
    62     page_range = property(_get_page_range)
       
    63 
       
    64 class QuerySetPaginator(Paginator):
       
    65     """
       
    66     Like Paginator, but works on QuerySets.
       
    67     """
       
    68     def _get_count(self):
       
    69         if self._count is None:
       
    70             self._count = self.object_list.count()
       
    71         return self._count
       
    72     count = property(_get_count)
       
    73 
       
    74 class Page(object):
       
    75     def __init__(self, object_list, number, paginator):
       
    76         self.object_list = object_list
       
    77         self.number = number
       
    78         self.paginator = paginator
       
    79 
       
    80     def __repr__(self):
       
    81         return '<Page %s of %s>' % (self.number, self.paginator.num_pages)
       
    82 
       
    83     def has_next(self):
       
    84         return self.number < self.paginator.num_pages
       
    85 
       
    86     def has_previous(self):
       
    87         return self.number > 1
       
    88 
       
    89     def has_other_pages(self):
       
    90         return self.has_previous() or self.has_next()
       
    91 
       
    92     def next_page_number(self):
       
    93         return self.number + 1
       
    94 
       
    95     def previous_page_number(self):
       
    96         return self.number - 1
       
    97 
       
    98     def start_index(self):
       
    99         """
       
   100         Returns the 1-based index of the first object on this page,
       
   101         relative to total objects in the paginator.
       
   102         """
       
   103         return (self.paginator.per_page * (self.number - 1)) + 1
       
   104 
       
   105     def end_index(self):
       
   106         """
       
   107         Returns the 1-based index of the last object on this page,
       
   108         relative to total objects found (hits).
       
   109         """
       
   110         if self.number == self.paginator.num_pages:
       
   111             return self.paginator.count
       
   112         return self.number * self.paginator.per_page
       
   113 
       
   114 class ObjectPaginator(Paginator):
       
   115     """
       
   116     Legacy ObjectPaginator class, for backwards compatibility.
       
   117 
       
   118     Note that each method on this class that takes page_number expects a
       
   119     zero-based page number, whereas the new API (Paginator/Page) uses one-based
       
   120     page numbers.
       
   121     """
       
   122     def __init__(self, query_set, num_per_page, orphans=0):
       
   123         Paginator.__init__(self, query_set, num_per_page, orphans)
       
   124         import warnings
       
   125         warnings.warn("The ObjectPaginator is deprecated. Use django.core.paginator.Paginator instead.", DeprecationWarning)
       
   126 
       
   127         # Keep these attributes around for backwards compatibility.
       
   128         self.query_set = query_set
       
   129         self.num_per_page = num_per_page
       
   130         self._hits = self._pages = None
       
   131 
       
   132     def validate_page_number(self, page_number):
       
   133         try:
       
   134             page_number = int(page_number) + 1
       
   135         except ValueError:
       
   136             raise InvalidPage
       
   137         return self.validate_number(page_number)
       
   138 
       
   139     def get_page(self, page_number):
       
   140         try:
       
   141             page_number = int(page_number) + 1
       
   142         except ValueError:
       
   143             raise InvalidPage
       
   144         return self.page(page_number).object_list
       
   145 
       
   146     def has_next_page(self, page_number):
       
   147         return page_number < self.pages - 1
       
   148 
       
   149     def has_previous_page(self, page_number):
       
   150         return page_number > 0
       
   151 
       
   152     def first_on_page(self, page_number):
       
   153         """
       
   154         Returns the 1-based index of the first object on the given page,
       
   155         relative to total objects found (hits).
       
   156         """
       
   157         page_number = self.validate_page_number(page_number)
       
   158         return (self.num_per_page * (page_number - 1)) + 1
       
   159 
       
   160     def last_on_page(self, page_number):
       
   161         """
       
   162         Returns the 1-based index of the last object on the given page,
       
   163         relative to total objects found (hits).
       
   164         """
       
   165         page_number = self.validate_page_number(page_number)
       
   166         if page_number == self.num_pages:
       
   167             return self.count
       
   168         return page_number * self.num_per_page
       
   169 
       
   170     def _get_count(self):
       
   171         # The old API allowed for self.object_list to be either a QuerySet or a
       
   172         # list. Here, we handle both.
       
   173         if self._count is None:
       
   174             try:
       
   175                 self._count = self.object_list.count()
       
   176             except TypeError:
       
   177                 self._count = len(self.object_list)
       
   178         return self._count
       
   179     count = property(_get_count)
       
   180 
       
   181     # The old API called it "hits" instead of "count".
       
   182     hits = count
       
   183 
       
   184     # The old API called it "pages" instead of "num_pages".
       
   185     pages = Paginator.num_pages