web/lib/django/core/paginator.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 from math import ceil
       
     2 
       
     3 class InvalidPage(Exception):
       
     4     pass
       
     5 
       
     6 class PageNotAnInteger(InvalidPage):
       
     7     pass
       
     8 
       
     9 class EmptyPage(InvalidPage):
       
    10     pass
       
    11 
       
    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
       
    19 
       
    20     def validate_number(self, number):
       
    21         "Validates the given 1-based page number."
       
    22         try:
       
    23             number = int(number)
       
    24         except ValueError:
       
    25             raise PageNotAnInteger('That page number is not an integer')
       
    26         if number < 1:
       
    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:
       
    30                 pass
       
    31             else:
       
    32                 raise EmptyPage('That page contains no results')
       
    33         return number
       
    34 
       
    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:
       
    41             top = self.count
       
    42         return Page(self.object_list[bottom:top], number, self)
       
    43 
       
    44     def _get_count(self):
       
    45         "Returns the total number of objects, across all pages."
       
    46         if self._count is None:
       
    47             try:
       
    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)
       
    54         return self._count
       
    55     count = property(_get_count)
       
    56 
       
    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:
       
    61                 self._num_pages = 0
       
    62             else:
       
    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)
       
    67 
       
    68     def _get_page_range(self):
       
    69         """
       
    70         Returns a 1-based range of pages for iterating through within
       
    71         a template for loop.
       
    72         """
       
    73         return range(1, self.num_pages + 1)
       
    74     page_range = property(_get_page_range)
       
    75 
       
    76 QuerySetPaginator = Paginator # For backwards-compatibility.
       
    77 
       
    78 class Page(object):
       
    79     def __init__(self, object_list, number, paginator):
       
    80         self.object_list = object_list
       
    81         self.number = number
       
    82         self.paginator = paginator
       
    83 
       
    84     def __repr__(self):
       
    85         return '<Page %s of %s>' % (self.number, self.paginator.num_pages)
       
    86 
       
    87     def has_next(self):
       
    88         return self.number < self.paginator.num_pages
       
    89 
       
    90     def has_previous(self):
       
    91         return self.number > 1
       
    92 
       
    93     def has_other_pages(self):
       
    94         return self.has_previous() or self.has_next()
       
    95 
       
    96     def next_page_number(self):
       
    97         return self.number + 1
       
    98 
       
    99     def previous_page_number(self):
       
   100         return self.number - 1
       
   101 
       
   102     def start_index(self):
       
   103         """
       
   104         Returns the 1-based index of the first object on this page,
       
   105         relative to total objects in the paginator.
       
   106         """
       
   107         # Special case, return zero if no items.
       
   108         if self.paginator.count == 0:
       
   109             return 0
       
   110         return (self.paginator.per_page * (self.number - 1)) + 1
       
   111 
       
   112     def end_index(self):
       
   113         """
       
   114         Returns the 1-based index of the last object on this page,
       
   115         relative to total objects found (hits).
       
   116         """
       
   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