13

Aug

Filed in Code, Django |

One of the tasks that I seem to repeatedly do across multiple projects, is extend the built-in paginator from Django. The built-in is fairly nice, but it’s quite honestly extremely confusing as you have to pass around a paginator instance as well as a paginator.page() instance in order to get useful pagination inside of a template.

After the Nth time of someone coming up with questions about the paginator, and seeing Brian Rosner’s not-very-smart “smart_page_range” code (his words, not mine!), I figured it might be useful to throw this out to the public.

from django.core.paginator import QuerySetPaginator, InvalidPage
 
__all__ = ('BetterQuerySetPaginator', 'InvalidPage')
 
class BetterQuerySetPaginator(QuerySetPaginator):
    """
    An enhanced version of the QuerySetPaginator.
 
    >>> my_objects = BetterQuerySetPaginator(queryset, 25)
    >>> page = 1
    >>> context = {
    >>>     'my_objects': my_objects.get_context(page),
    >>> }
    """
    def get_context(self, page, range_gap=5):
        try:
            page = int(page)
        except (ValueError, TypeError), exc:
            raise InvalidPage, exc
 
        if page > 5:
            start = page-range_gap
        else:
            start = 1
 
        if page < self.num_pages-range_gap:
            end = page+range_gap+1
        else:
            end = self.num_pages+1
 
        paginator = self.page(page)
 
        context = {
            'page_range': range(start, end),
            'objects': paginator.object_list,
            'num_pages': self.num_pages,
            'page': page,
            'has_previous': paginator.has_previous(),
            'has_next': paginator.has_next(),
            'previous_page': paginator.previous_page_number(),
            'next_page': paginator.next_page_number(),
            'is_first': page == 1,
            'is_last': page == self.num_pages,
        }
 
        return context
  • http://moto-notes.com eXt

    Nice code. Thanks. One thing to point out is that ‘QuerySetPaginator’ is now replaced by ‘Paginator’ class. ‘QuerySetPaginator’ exists for backwards-compatibility only.

  • Doug Napoleone

    I would also like to point out the django-pagination app.
    http://django-pagination.googlecode.com/

    which is insanely powerful. I highly recommend people check it out.

  • Dyadya Zed

    Doug, I had problems using django-pagination and django-tagging. It didn’t display any pagination elements for generic views.

  • Doug Napoleone

    @Daydya

    That is because django-tagging does not return a queryset. (django-pagination will soon error if you do not give it a queryset). Even the builtin django pagination has problems with tagging. We have a hack workaround for this in pinax, using the result from the tagging manager to construct a new queryset object; but that defeats part of the purpose of pagination.

    django-tagging backs off to the cursor with custom SQL for the majority of it work and returns a list of dicts or objects (depending on the manager call). This causes many problems for tagging in general. With the queryset re-factoring some of this can be fixed. In short it is more a problem with tagging than with pagination.

  • http://simonwillison.net/ Simon Willison

    Populating the context is only half of the problem though – the really fiddly bit is the template code, HTML and CSS to support it.

    I’d love to see Django ship with half a dozen or so pre-configured templates for common styles of pagination (Flickr style, Digg style etc) along with well commented, easily customised CSS to style them. I reckon that could cover 95% of people’s needs, and make it trivial for the remaining 5% to get exactly what they want.

  • http://www.appoil.com sean

    @Simon

    It’s really a great idea to provide common styles of pagination. Hope to see them in the trunk soon.

  • http://justinlilly.com Justin Lilly

    I think calling Brian out is unnecessary. The post would have had the same value if you either left out Brian’s name or excluded the not-so-smart jab.

  • http://www.eflorenzano.com/ Eric Florenzano

    @Simon

    Even though right now django-pagination only has one kind of pagination, the original goal of that project was to be exactly what you said. If you have any code sitting around, I’d love to include it into django-pagination.

  • http://www.eflorenzano.com/ Eric Florenzano

    Also, I agree with Justin: calling Brian out was really bad form.

  • David

    Sorry, the context sucked. I was quoting Brian on that one :)

  • http://incuna.com/ James Turnbull

    @Simon, Eric:

    All the styles on this page coincidentaly have the same class names as django-pagination:

    http://www.mis-algoritmos.com/2007/03/16/some-styles-for-your-pagination/

    Handy!

  • Dyadya Zed

    Just forgot to mention. I’d would like to see option in paginator to return numbers in reversed order. Like google older/newer paginator, but with reversed numbers:

    <>

    Of course paginator code can be hacked, but it’s better to have an option.

  • Dyadya Zed

    Sorry, this site broken my example. Here is repost:

  • Dyadya Zed

    Sorry, this site broken my example. Here is repost:

    Newer posts 4 3 2 1 Older posts

blog comments powered by Disqus