Login

Digg-like pagination

Author:
SmileyChris
Posted:
May 25, 2009
Language:
Python
Version:
1.0
Score:
2 (after 2 ratings)

My take on digg-like pagination.

Save the code as 'templatetags/pagination_nav.py' in one of your apps.

It relies on a 'pagination_nav.html' template. Here is a base template:

{% if pages %}
<div class="bottom-pagination-nav">
{% if previous_url %}<a href="{{ previous_url }}">{% else %}<span>{% endif %}&laquo; Previous{% if previous_url %}</a>{% else %}</span>{% endif %}
{% for group in pages %}
{% for page in group %}
{% if page.current %}<span>{{ page.number }}</span>{% else %}<a href="{{ page.url }}">{{ page.number }}</a>{% endif %}
{% endfor %}
{% if not forloop.last %}<span>...</span>{% endif %}
{% endfor %}
{% if next_url %}<a href="{{ next_url }}">{% else %}<span>{% endif %}Next &raquo;{% if next_url %}</a>{% else %}</span>{% endif %}
</div>
{% endif %}
  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
import re
from django import template
from django.http import QueryDict


ADJACENT = 3
CAPS = 1
RE_URL = re.compile('(.*)1(.*)')
register = template.Library()


def page_separator(current, count, adjacent=ADJACENT, caps=CAPS):
    if current < adjacent + 1:
        adjacent += adjacent - current + 1
    elif count - current < adjacent:
        adjacent += adjacent - (count - current)
    bits = []
    if current > (1 + adjacent + caps):
        if caps:
            bits.append(range(1, caps+1))
        start = current - adjacent
    else:
        start = 1
    if current + adjacent < count - caps:
        end = current + adjacent
    else:
        end = count
    bits.append(range(start, end+1))
    if end != count:
        if caps:
            bits.append(range(count-caps+1, count+1))
    return bits


def get_page_context(page, url_func, adjacent, caps):
    if not page:
        return {}
    current = page.number
    count = page.paginator.num_pages
    if count < 2:
        return {}
    pages = []
    for page_group in page_separator(current, count, adjacent, caps):
        group = []
        for number in page_group:
            url = url_func(number)
            if not url:
                return {}
            group.append({'url': url, 'number': number,
                          'current': page.number==number})
        pages.append(group)
    c = {'pages': pages}
    if current > 1:
        c['previous_url'] = url_func(current-1)
    if current < count:
        c['next_url'] = url_func(current+1)
    return c


@register.inclusion_tag('pagination-nav.html')
def pagination_nav(page, url, first_page_url=None, adjacent=ADJACENT, caps=CAPS):
    """
    Generate Digg-like pagination navigation for URLs like "/entries/page/5/".

    Required arguments:

    page
        A ``django.core.paginator`` ``Page`` object
    url
        An example URL to navigate to page 1 (the last character ``"1"`` in the
        URL gets replaced with the actual page number)

    Optional arguments:

    first_page_url
        An alternate first page exact URL
    adjacent
        The minimum number of pages to show either side of the current page
        (defaults to ``3``)
    caps
        The number of pages to show at either end (defaults to ``1``)
    """
    def make_url(number):
        if number == 1 and first_page_url:
            return first_page_url
        match = RE_URL.match(url)
        if not match:
            raise template.TemplateSyntaxError(
                'URL did not contain the character "1" (which gets replaced with '
                'the actual page number).'
            )
        start, end = match.groups()
        return '%s%s%s' % (start, number, end)
    return get_page_context(page, make_url, adjacent, caps)


@register.inclusion_tag('pagination-nav.html')
def pagination_nav_qs(page, url='', querydict=None, url_attr='page',
                      adjacent=ADJACENT, caps=CAPS):
    """
    Generate Digg-like pagination navigation for URLs like "/entries/?page=5".

    Required arguments:

    page
        A django.core.paginator Page object

    Optional arguments:

    url
        The base url (defaults to a blank string)
    querydict
        Usually ``request.GET`` (defaults to an empty ``QueryDict``)
    url_attr
        The querystring attribute to change (defaults to ``'page'``)
    adjacent
        The minimum number of pages to show either side of the current page
        (defaults to ``3``)
    caps
        The number of pages to show at either end (defaults to ``1``)
    """
    querydict = querydict or QueryDict('')
    def make_url(number):
        qs = querydict.copy()
        if number == 1:
            qs.pop(url_attr, None)
        else:
            qs[url_attr] = number
        qs = qs.urlencode()
        if qs:
            qs = '?%s' % qs
        if not url and not qs:
            return '.'
        return '%s%s' % (url or '', qs)
    return get_page_context(page, make_url, adjacent, caps)

More like this

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

Comments

Please login first before commenting.