Pagination Alphabetically compatible with paginator_class

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import string
from django.core.paginator import InvalidPage, EmptyPage

class NamePaginator(object):
    """Pagination for string-based objects"""

    def __init__(self, queryset, paginate_by=25, orphans=0, allow_empty_first_page=True):
        # We ignore allow_empty_first_page and orphans, just here for compliance
        self.pages = []
        self.object_list = queryset
        self.count = len(self.object_list)

        # chunk up the objects so we don't need to iterate over the whole list for each letter
        chunks = {}

        # we sort them by the first model ordering key
        for obj in self.object_list:
            if queryset: obj_str = unicode(getattr(obj, obj._meta.ordering[0]))
            else: obj_str = unicode(obj)

            # some of my models had "first_name" "last_name" sorting, and some first_names
            # were empty so if it fails you can try sorting by the second ordering key
            # which worked for me but do your own thing
            try:
                letter = unicode.upper(obj_str[0])
            except:
                obj_str = unicode(getattr(obj, obj._meta.ordering[1]))
                letter = unicode.upper(obj_str[1])

            if letter not in chunks: chunks[letter] = []

            chunks[letter].append(obj)

        # the process for assigning objects to each page
        current_page = NamePage(self)

        for letter in string.ascii_uppercase:
            if letter not in chunks:
                current_page.add([], letter)
                continue

            sub_list = chunks[letter] # the items in object_list starting with this letter

            new_page_count = len(sub_list) + current_page.count
            # first, check to see if sub_list will fit or it needs to go onto a new page.
            # if assigning this list will cause the page to overflow...
            # and an underflow is closer to per_page than an overflow...
            # and the page isn't empty (which means len(sub_list) > per_page)...
            if new_page_count > paginate_by and \
                    abs(paginate_by - current_page.count) < abs(paginate_by - new_page_count) and \
                    current_page.count > 0:
                # make a new page
                self.pages.append(current_page)
                current_page = NamePage(self)

            current_page.add(sub_list, letter)

        # if we finished the for loop with a page that isn't empty, add it
        if current_page.count > 0: self.pages.append(current_page)

    def page(self, num):
        """Returns a Page object for the given 1-based page number."""
        if len(self.pages) == 0:
            return None
        elif num > 0 and num <= len(self.pages):
            return self.pages[num-1]
        else:
            raise InvalidPage

    @property
    def num_pages(self):
        """Returns the total number of pages"""
        return len(self.pages)

class NamePage(object):
    def __init__(self, paginator):
        self.paginator = paginator
        self.object_list = []
        self.letters = []

    @property
    def count(self):
        return len(self.object_list)

    @property
    def start_letter(self):
        if len(self.letters) > 0:
            self.letters.sort(key=str.upper)
            return self.letters[0]
        else: return None

    @property
    def end_letter(self):
        if len(self.letters) > 0:
            self.letters.sort(key=str.upper)
            return self.letters[-1]
        else: return None

    @property
    def number(self):
        return self.paginator.pages.index(self) + 1

    # just added the methods I needed to use in the templates
    # feel free to add the ones you need too
    def has_other_pages(self):
        return len(self.object_list) > 0

    def has_previous(self):
        return self.paginator.pages.index(self)

    def has_next(self):
        return self.paginator.pages.index(self) + 2

    def next_page_number(self):
        return self.paginator.pages.index(self) + 2

    def previous_page_number(self):
        return self.paginator.pages.index(self)

    def add(self, new_list, letter=None):
        if len(new_list) > 0: self.object_list = self.object_list + new_list
        if letter: self.letters.append(letter)

    def __repr__(self):
        if self.start_letter == self.end_letter:
            return self.start_letter
        else:
            return '%c-%c' % (self.start_letter, self.end_letter)

More like this

  1. Digg-like pagination by SmileyChris 3 years, 12 months ago
  2. Paginator TemplateTag by trbs 5 years, 1 month ago
  3. Add querystring parameters to path (template tag) by spenoir 4 months ago
  4. load m2m fields objects by dirol 2 years, 11 months ago
  5. Page numbers with ... like in Digg by Ciantic 4 years, 1 month ago

Comments

stevenjoseph (on April 19, 2012):

Thanks for this, made a few modifications which might be useful:

16c16,17
<             if on: obj_str = str(getattr(obj, on))
---
>             if on !=None:
>                 obj_str = str(getattr(obj, on)) if isinstance(obj,(str,unicode)) else obj[on]
18,20c19,21       
<             letter = str.upper(obj_str[0])   
--- 
>             letter = obj_str[0].upper()

#

siblek31 (on April 13, 2013):

Perancis memiliki anggaran dunia terbesar kelima nominal militer, [13] serta (dalam hal personil) militer terbesar di Uni Eropa, [rujukan?] Kekuatan deployable terbesar ketiga di NATO, dan militer-26 terbesar di dunia. Perancis juga memiliki stockpile terbesar ketiga senjata nuklir di dunia [14] - dengan sekitar 300 hulu ledak tips cepat hamil aktif sejak 25 Mei 2010 -. Dan dunia terbesar kedua diplomatik korps (di belakang Amerika Serikat) [15] Perancis adalah anggota pendiri PBB, salah satu dari lima anggota tetap Dewan Keamanan PBB, soal ulangan harian sd dan anggota Francophonie, G8, G20, NATO, OECD, WTO, dan Uni Latin. Ini juga merupakan pendiriteknisi komputer dan negara anggota terkemuka dari Uni Eropa dan negara kursus bahasa inggris Uni Eropa terbesar berdasarkan wilayah. [16] Pada 2013, Prancis tercatat 20 pada Indeks mendapatkan uang dari internet Pembangunan Manusia dan, pada tahun 2010, 24 pada Indeks Persepsi Korupsi.

#

(Forgotten your password?)