~canonical-django/canonical-django/project-template

« back to all changes in this revision

Viewing changes to trunk/python-packages/django/core/paginator.py

  • Committer: Matthew Nuzum
  • Date: 2008-11-13 05:46:03 UTC
  • Revision ID: matthew.nuzum@canonical.com-20081113054603-v0kvr6z6xyexvqt3
adding to version control

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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