Paginator Tag

 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
from django import template

register = template.Library()

def paginator(context, adjacent_pages=2):
    """
    To be used in conjunction with the object_list generic view.

    Adds pagination context variables for use in displaying first, adjacent and
    last page links in addition to those created by the object_list generic
    view.

    """
    page_numbers = [n for n in \
                    range(context['page'] - adjacent_pages, context['page'] + adjacent_pages + 1) \
                    if n > 0 and n <= context['pages']]
    return {
        'hits': context['hits'],
        'results_per_page': context['results_per_page'],
        'page': context['page'],
        'pages': context['pages'],
        'page_numbers': page_numbers,
        'next': context['next'],
        'previous': context['previous'],
        'has_next': context['has_next'],
        'has_previous': context['has_previous'],
        'show_first': 1 not in page_numbers,
        'show_last': context['pages'] not in page_numbers,
    }

register.inclusion_tag('paginator.html', takes_context=True)(paginator)

Comments

derivin (on March 12, 2007):

VERY COOL! This saved me a bunch of work. I did make a minor change (just a style difference really):

page_numbers = range(max(0, context['page']-adjacent_pages), min(context['pages'], context['page']+adjacent_pages)+1)

#

RichardBronosky (on July 18, 2007):

(This is my first python contribution. I hope it is correct and useful. Please email me at [myFirstName]@[myLastName].com with any corrections.)

I've patched this to add:

  • results_this_page - On the last page, there will usually be less than results_per_page results to display
  • first_this_page - the starting point of the slice that object_list represents
  • last_this_page - the ending point of the slice that object_list represents

As a result, I can use the following paginator.html template:

Showing {{ first_this_page }}-{{ last_this_page }} of {{ hits }} hits on page {{ page }} of {{ pages }}

To get these results on the pages:

  1. Showing 1-20 of 46 hits on page 1 of 3
  2. Showing 21-40 of 46 hits on page 2 of 3
  3. Showing 41-46 of 46 hits on page 3 of 3

I'd also like to throw out a NOTICE that in order to implement this snippet, you must follow http://www.djangoproject.com/documentation/templates_python/#extending-the-template-system As a new user to both Python and Django, I was trying to out this in my views.py, which does not work. I hope that saves someone from wasting the time I did.

BEGIN PATCH

--- pagination.original.py  2007-07-18 13:33:02.000000000 -0400
+++ pagination.py   2007-07-18 13:31:10.000000000 -0400
@@ -2,7 +2,9 @@

 register = template.Library()

+@register.inclusion_tag('paginator.html', takes_context=True)
 def paginator(context, adjacent_pages=2):
+    print context
     """ 
     To be used in conjunction with the object_list generic view.

@@ -14,9 +16,14 @@
     page_numbers = [n for n in \
                     range(context['page'] - adjacent_pages, context['page'] + adjacent_pages + 1) \
                     if n > 0 and n <= context['pages']]
+    results_this_page = context['object_list'].__len__()
+    range_base = ((context['page'] - 1) * context['results_per_page'])
     return {
         'hits': context['hits'],
         'results_per_page': context['results_per_page'],
+        'results_this_page': results_this_page,
+        'first_this_page': range_base + 1,
+        'last_this_page': range_base + results_this_page,
         'page': context['page'],
         'pages': context['pages'],
         'page_numbers': page_numbers,
@@ -27,5 +34,3 @@
         'show_first': 1 not in page_numbers,
         'show_last': context['pages'] not in page_numbers,
     }   
-   
-register.inclusion_tag('paginator.html', takes_context=True)(paginator)

#

RichardBronosky (on July 18, 2007):

Oh yea, in that patch I also changed it to use a decorator. I don't know much about python, but I think that is appropriate.

#

RichardBronosky (on July 20, 2007):

I think this is a very important improvement. I hate having to keep adding to the context list as my app matured. I should only have to add new context to my views. So, I came up with this.

To pass the entire context with modifications/additions to the template of an inclusion tag, you can use:

return dict(context.dicts[5], existing_key='val to overwrite old val', new_key='new_val')

#

msurdi (on April 10, 2008):

Could you please post a "view" example for this snippet?

#

Romain Hardouin (on June 11, 2008):

Example of a view

def news_by_category(request, category_name, page=1):
    category = get_object_or_404(Category, name__iexact=category_name)
    news = News.objects.select_related(depth=1).filter(category=category.id)

    from django.views.generic import list_detail
    return list_detail.object_list(request,
                               queryset = news,
                               template_name = 'template_name.html',
                               paginate_by = 10,
                               page = page,
                               allow_empty = False,
                               extra_context = {'category': category}
                           )

View parameters and paginator.html?

How this template could be design in order to paginate any resource?

For instance, if you want to paginate instance of News model, then instance of Category model and so forth, links in paginator must be dynamics. Till now I can't figure out how to do this neat and clean.

  • paginator.html -- and thus its template Context -- must be 'view aware' to build links
  • Then, with a paginator template view aware, we can use the template tag {% url %} to generate links
  • Sounds good but this template tag requires view parameters...

So, how transmit those parameters to the template?

#

mzee (on October 8, 2008):

nice stuff

#

sugi (on November 14, 2008):

Can you suggest some more examples on using templatetags and how to use URLS for this.

Help me

#

sylvain (on January 11, 2009):

For an example, see the djblets datagrid template tag: http://svn.navi.cx/misc/trunk/djblets/djblets/datagrid/

Note that in Django 1.0, the paginator attributes should be accessed through page_obj and paginator.

So the paginator function should look like:

page_obj = context['page_obj']
paginator = context['paginator']
...
return {
    'page_obj': page_obj,
    'paginator': paginator,
    'page_numbers': page_nums,
    'show_last': context['pages'] not in page_nums,
}

And the template can access properties through page_obj and paginator. For instance, instead of {{ pages }} you would use {{ paginator.num_pages }}

#

(Forgotten your password?)

You may use Markdown syntax here, but raw HTML will be removed.