Login

Pagination Template Tag

Author:
ioan1k
Posted:
September 18, 2009
Language:
Python
Version:
1.1
Score:
2 (after 2 ratings)

Django Pagination Template Tag that allows unlimited customization to the current Django Pagination.

Automatically creates template variables that can be used to display the pagination in any format that you can desire such as

Previous 1 2 3 [ 4 ] 5 6 7 Next

First Previous 12 14 15 16 17 [ 18 ] 19 20 22 25 Next Last

Showing 25 of 80 Results

First Page 23 27 30 33 [ 36 ] 38 41 44 50 Next Last

  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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
""" XPaginate FILE """

from math import *
from django import template
from django.template import Library, Node

register = template.Library()

"""
    Django Pagination Template Tag
    Allows for complete customization on pagination display
    Renders pages a template context with the following vars avaliable
    
    total_items:        List of total pagination items
    current:            Current Page pagination is on
    per_page:           How many items per page
    has_previous:       Has links to display that go to previous pages
    has_next:           Has links to display that go fowards pages
    foward_pages:       Dictionary contaning forward page numbers
    previous_links:     Dictionary containing previous page numbers
    has_jump_previous:  Has links that "jump" backwords through previous page numbers
    has_jump_foward:    Has page numbers that "jump" through foward page numbers
    foward_page_jump:   Dictionary containg list of forward jumping page numbers
    foward_page_jump:   Dictionary containg list of previous jumping page numbers
    show_last:          Boolean that smartly determains the current page location to display a link to the last page
    show_last:          Boolean that smartly determains the current page location to display a link to the first page
"""

class XPaginate:
    def __init__(self, uri, total_items, current, per_page, show, jump, range):
        self.total_items = int(total_items)
        self.current = int(current)
        self.per_page = int(per_page)
        self.show = int(show)
        self.jump = jump
        self.range = int(range)
        self.jump_calc = 2
        self.uri = uri
        
        if self.current is 0:
            self.current = 1
        
        # Total number of pages
        self.total_pages = int(ceil(float(total_items) / float(per_page)))
        
        if self.current is not 1:
            self.has_previous = True
        else:
            self.has_previous = False
            
        if self.current < self.total_pages:
            self.has_next = True
        else:
            self.has_next = False
        
        # Get the first foward set of page links if avaliable
        self.foward_pages = []
        if self.has_next:
            a = self.current
            self.foward = int(self.current + 1)
            total = 0
            while total is not self.show:
                a = a + 1
                total = total + 1
                if a <= self.total_pages:
                    if total is self.show:
                        last_foward = a
                    self.foward_pages.append(a)
            if last_foward is self.total_pages or (last_foward + self.show) > self.total_pages:
                self.show_last = False
            else:
                self.show_last = True
            #print self.foward_pages, '\n'
        
        # GET the set of previous pages
        self.previous_pages = []
        if self.has_previous:
            self.previous = int(self.current - 1)
            a = int((self.current - 1) - self.show)
            total = 0
            while total is not self.show:
                a = a + 1
                total = total + 1
                if a <= self.total_pages and a is not 0:
                    if total is self.show:
                        last_previous = int(a)
                    self.previous_pages.append(a)
            
            #print self.previous_pages, '\n'
                    
        # Calculate if we have the ability to jump foward pages
        if self.jump and (self.current - ceil(self.range * self.jump_calc)) >= 2:
            self.has_jump_previous = True
        else:
            self.has_jump_previous = False
            
        if self.jump and (self.current + ceil(self.range * self.jump_calc)) < self.total_pages:
            self.has_jump_foward = True
        else:
            self.has_jump_foward = False
        
        ## Calc Jump pages foward
        
        if self.has_jump_foward:
            a = last_foward
            total = 0
            self.foward_page_jump = []
            while total is not self.range:
                total = total + 1
                a = int(float(ceil(a * self.jump_calc)))
                if a <= self.total_pages:
                    self.foward_page_jump.append(a)
            
        if self.has_jump_previous:
            a = int(last_previous)
            total = 0
            self.previous_page_jump = []
            while total is not self.range:
                total = total + 1
                a = int(ceil(a / float(self.jump_calc)))
                if a <= self.total_pages and a >= 2:
                    self.previous_page_jump.insert(0, a)
        
class RenderXPage(Node):
    def __init__(self, uri, total_items, current, per_page, show, jump, range):
        self.total_items = template.Variable(total_items)
        self.current = template.Variable(current)
        self.uri = template.Variable(uri)
        if not per_page:
            self.per_page = 10
        else:
            self.per_page = per_page
        if not show:
            self.show = 3
        else:
            self.show = show
        if not jump:
            self.jump = True
        else:
            self.jump = jump
        if not range:
            self.range = 3
        else:
            self.range = range

    def render(self, context):
        
        try:
            context['xpaginate'] = XPaginate(self.uri.resolve(context), self.total_items.resolve(context), self.current.resolve(context), self.per_page, self.show, self.jump, self.range)
        except:
            pass
        
        return ''

def xpaginator(parser, token):
    tokens = token.split_contents()
    return RenderXPage(tokens[1], tokens[2], tokens[3], tokens[4], tokens[5], tokens[6], tokens[7])

register.tag('xpaginator', xpaginator)

""" XPaginate Tempalte File """
<div class="pagination">
{% if xpaginate.has_previous %}
<a href="{{ xpaginate.uri }}{{ xpaginate.previous }}"><< Previous</a>
    {% if xpaginate.has_jump_previous %}
        {% for a in xpaginate.previous_page_jump %}
            <a href="{{ xpaginate.uri }}{{ a }}">{{ a }}</a> ... 
        {% endfor %}
    {% endif %}
    {% for a in xpaginate.previous_pages %}
        <a href="{{ xpaginate.uri }}{{ a }}">{{ a }}</a>
    {% endfor %}
{% endif %}

<span class="current">[ {{ xpaginate.current }} ]</span>

{% if xpaginate.has_next %}
    {% for a in xpaginate.foward_pages %}
        <a href="{{ xpaginate.uri }}{{ a }}">{{ a }}</a>
    {% endfor %}
    {% if xpaginate.show_last %}
        ... <a href="{{ xpaginate.uri }}{{ xpaginate.total_pages }}">{{ xpaginate.total_pages }}</a>
    {% endif %}
    <a href="{{ xpaginate.uri }}{{ xpaginate.foward }}">Next >></a>
{% endif %}
</div>


""" DIRECTORY STRUCTURE """
|-- xpaginate
  |-- templatetags
    |-- xpaginate.py
|-- path/to/view/templates
  |-- xpaginate.html

""" USAGE EXAMPLE """

NOTE:
You must add **xpaginate** to you INSTALLED_APPS list

[ view.py ]
from urlgen.urlgen import urlGen

cards = Card.objects.all().order_by('name')

items = cards.count()
paginator = Paginator(cards, 16) # Show 25 contacts per page

# Make sure page request is an int. If not, deliver first page.
try:
    page = int(request.GET.get('page', '1'))
except ValueError:
    page = 1

# If page request (9999) is out of range, deliver last page of results.
try:
    cards = paginator.page(page)
except (EmptyPage, InvalidPage):
    cards = paginator.page(paginator.num_pages)
    
"""
USES URI Generator Snippet
http://www.djangosnippets.org/snippets/1734/
"""

url = urlGen()

pageURI = url.generate('page', request.GET)

cards = cards.object_list

return render_to_response('template.html', {'cards': cards,
                                            'total_item': items,
                                            'pageURI': pageURI,
                                            'current': page         
                                            }, context_instance=RequestContext(request))

[ template.html ]
{% for card in cards %}
    <li>
        <span>{{ card.name }}</span>
    </li>
{% endfor %}
{% load xpaginator %}
{% xpaginator pageURI total_item current 16 1 True 1 %}
{% include "xpaginate.html" %}

Template Tag accepts the following parameters
uri: URI to use for link, EX ?page=
total_items: total number of items
current: current page that user is on
per_page: items displayed per page
show: OPTIONAL: how many links to display on left and right of current page
jump: OPTIONAL: create a list of links that jump through pages increasing by 1.2* the current
range: Number of jumped links to calculate and display on for forward and reverse

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

Michele (on November 10, 2010):

Hi, first of all thanks for the snippet. I found a bug depending on the value assumed by "self.current" variable. If self.current is greater than 256, the test "self.current is not self.total_pages" (at line 57) became true...why not use the '!=' operator?

#

ioan1k (on December 7, 2010):

Thanks for the find, snippet updated to accommodate.

#

miernik (on April 12, 2012):

Isn't this some error?

Two variables with the same name and different descriptions? And what about showing the link to the first page?

show_last:          Boolean that smartly determains the current page location to display a link to the last page
show_last:          Boolean that smartly determains the current page location to display a link to the first page

Besides that you don't write "determains" but "determines".

#

Please login first before commenting.