Login

Paginator TemplateTag

Author:
trbs
Posted:
March 28, 2008
Language:
Python
Version:
.96
Score:
6 (after 6 ratings)

Paginator TemplateTag

TemplateTag to use the new Paginator class directly from a template. The paginate template tags take the following options:

  1. list or queryset to paginate
  2. number of pages
  3. [optionaly] name of the Paginator.Page instance; prefixed by keyword 'as'
  4. [optionaly] name of the http parameter used for paging; prefixed by keyword 'using'

If you want to specify the parameter name with the keyword 'using' you must use the 'as' keyword as well. The default name of the paging variable is "page" and the paginator (the class that knows about all the pages is set in the context as "page_set". This follows the naming scheme of the ORM mapper for relational objects where "_set" is appended behind the variable name.

Usage, put the following in your template:

{% load paginate %}
{% get_blog_posts blog_category as posts %}
{% paginate posts 10 as page using page %}
<ul>
    {% for post in page.object_list %}
       <li>{{ post.title }}</li>
    {% endfor %}
</ul>
<div>
    {% if page.has_previous %}
    <a href="?page={{ page.previous_page_number }}">previous</a>
    {% endif %}
    <i>{{ page.number }} of {{ page_set.num_pages }}</i>
    {% if page.has_next %}
    <a href="?page={{ page.next_page_number }}">next</a>
    {% endif %}
</div>

The templatetag requires the request object to be present in the template context. This means that you need 'django.core.context_processors.request' added to settings.TEMPLATE_CONTEXT_PROCESSORS list or otherwise make sure that the templatetag can access the request object.

Comments are appreciated.

 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
from django import template
from django.db.models.query import QuerySet
from django.core.paginator import Paginator, QuerySetPaginator

register = template.Library()

class PaginateNode(template.Node):
    def __init__(self, objlist_name, nr_pages, var_name="page", param_name="page"):
        self.objlist_name = objlist_name
        self.nr_pages = nr_pages
        self.var_name = var_name
        self.param_name = param_name
    
    def render(self, context):
        try:
    	    list_or_queryset = context[self.objlist_name]
        except KeyError:
            return ''
        
    	if isinstance(list_or_queryset, QuerySet):
    	    paginator = QuerySetPaginator(list_or_queryset, self.nr_pages)
    	else:
    	    paginator = Paginator(list_or_queryset, self.nr_pages)
	
    	try:
    	    page = int(context['request'].REQUEST.get(self.param_name, 1))
    	except:
    	    page = 1

    	context[self.var_name] = paginator.page(page)
        context[self.var_name+"_set"] = paginator
        # backward compatibility injections into the context
    	context[self.var_name+"_count"] = paginator.count
    	context[self.var_name+"_num_pages"] = paginator.num_pages
    	context[self.var_name+"_page_range"] = paginator.page_range
    	return ''

def paginate(parser, token):
    try:
        tag_name, arg = token.contents.split(None, 1)
    except ValueError:
        raise template.TemplateSyntaxError, "%r tag requires arguments" % token.contents.split()[0]
    args = arg.split(None)
    var_name = "page"
    param_name = "page"
    try:
        objlist_name = args[0]
        nr_pages = args[1]
    except:
        raise template.TemplateSyntaxError, "%r tag invalid arguments" % token.contents.split()[0]
    if len(args)==2:
        pass
    elif len(args)==4 and args[2]=="as":
        var_name = args[3]
    elif len(args)==6 and args[4]=="using":
        var_name = args[3]
        param_name = args[5]
    else:
        raise template.TemplateSyntaxError, "%r tag invalid arguments" % token.contents.split()[0]
    try:
        nr_pages = int(nr_pages)
    except:
        raise template.TemplateSyntaxError, "%r tag invalid arguments" % token.contents.split()[0]
    return PaginateNode(objlist_name, nr_pages, var_name, param_name)
paginate = register.tag(paginate)

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, 2 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

wam (on April 16, 2008):

It probably merits mentioning that this tag depends on 'django.core.context_processors.request' being added to settings.TEMPLATE_CONTEXT_PROCESSORS, as this is a non-default configuration.

#

trbs (on April 19, 2008):

Thanks wam, text has been updated.

I've also added a new variable in the context called "page_set" or otherwise var_name+"_set" which contains the paginator object.

However since the paginator object does not implement __len__ and alikes so you cannot use {{ page_set|count }} instead you need the call the method {{ page_set.count }} or for example with the humanize contrib {{ page_set.count|apnumber }} .

The old names are still there for backwards compatibility. (But might be removed in the future as it clutters the context)

#

jaaay (on April 28, 2008):

I can't seem to get past a KeyError at the line:

list_or_queryset = context[self.objlist_name]

Which I would guess means I'm not passing the request object properly, but I do have django.core.context_processors.request added to settings.py. Is there something else that needs to be considered?

For what it's worth, Here's what I'm doing in the template: {% paginate category.product_set.active 10 %}

#

trbs (on June 1, 2008):

The snippet should be updated to lookup the variable with the template tags resolve_variable api.

Currently it requires you to put the list_or_queryset directly into the template context.

Since the other paginator tag seems to be far far more popular then mine, i will not spend much more time on this. If anyone wants to make a patch (and remind me of it, cause djangosnippets doesn't send mails) I'll happily apply it and update the snippet.

#

schraal (on July 10, 2008):

there's a bug in the template example code. the 'for' is closed by an 'endif'.

thanks for the snippet, I found it very usefull indeed for my usecase. hope you feel encouraged to keep maintaining the snippet.

#

trbs (on August 24, 2008):

thnx schraal, updated the example code. i'll hope to update the tag for 1.0 once it is released.

#

Please login first before commenting.