Django Aware

« | Home | »

Auto Adjusting Pagination As A Template Tag Plus Include

By Paul Kenjora | December 16, 2007

This post has been updated, the source code is available for download on Digg Style Pagination In Django Revisited.

Django has made me really lazy. I never in my wildest dreams imagined I’d be wanting something simpler than the pagination already built into Django, but it has happened. On several projects now I’ve run into an issue of too many pages to dsiplay to the user. Once you get over twenty or so pages the links start to look way messy. So I decided to write a function that would create nice sliding page ranges for the user. A bit like Digg.com does. That worked except I was frustrated that the the layout and templating of each set of links was difficult to synch up from the back end. So I eventually ended up creating a template tag I can include in my pages and quickly create pagination.

What Does Auto Adjusting Mean?

I’ve labeled the pagination tag "auto adjusting" because it is designed to give up to three easily readable ranges of pages for the user to click. Range one will always be page one through some configurable number (more on this later). Range two appears only if enough pages exist and the current page is far enough out, it shows a subset of pages in the neighborhood of the current page. Range three appears only if there are enough pages and shows the last few pages, also configurable. The three ranges are automatically created if required to ensure the user can easily navigate through large page numbers. For details on how to configure the ranges see the "Using The Pagination Template Tag" section at the bottom.

The Source

The code consists of a template tag and an HTML file. The template tag sets up thge page ranges and does all the math. The HTML file allows me to quickly customize the look and feel of the page links for any given site. I’ve also thrown in a sample CSS file for reference on formatting the look and feel of the links.

Source Download

Using The Pagination Template Tag

Making this part as simple and straight forward as possible was my main goal. To use the new tag simply load it, call the paginate function on any list, specify page number, entries per page, and the name of the resulting list. Once the call is made you can use the new list and include the "pagination.html" file to get a cool sliding set of pagination links. Dont forget to apply the style sheets.

For reference as the sample below illustrates the API for the pagination tag is:

{% paginate [query set to be paginated] [current page] [items per page] [length of page groups] [new paginated list] %}

Source Download

Conclusion

Dont worry about all the code under the hood. You only need to copy and paste each of the sample files and you’re ready to go, this works for any model type. The HTML and CSS files are yours to modify, the template tag code requires no modification. Now you can paginate any page on your Django site with a smart sliding set of page links in just 3 lines of code. If you’re worried about efficiency don’t be, Django queries are lazy, and the template tag only does one DB hit to fetch a count. Yes I could have used the native paginator for some of the math but it would have been overkill. Hope this is handy.

Topics: Template Tags | Comments

  • Ken
    I agree with Keir -- where can I download these files instead of copying, pasting them, then correcting for the lack of indentation in your little fold-down thing?
  • The file is available for download. See top of post.
  • Hey Paul, This looks great and just what I need for a project I am working on. Would you mind making the three files available for download or if they are available point me in the right direction.

    Thanks a lot.
  • FOr those of you asking how to limit each piece of the display. You can control which segments get displayed using the template HTML. Simply remove any of the loops to not use a specific segment of code.
  • Rick van Hattem
    Just a little tip, instead of using:
    return PaginationNode(tokens[1], tokens[2], tokens[3], tokens[4], tokens[5])

    You could also write
    return PaginationNode(*tokens[1:])

    Shorter, neater, and more flexible :)
  • Jon,

    Good catch, I've made the change you suggestd in the post code snippet.

    As far as small lists, play around with the "length of page groups" variable. When lists get small the code only populates the first or second group. If you have 10 or so pages then your groups need to be set to about 3. At that point you probably dont need the groups and one long list would be fine.

    Let me know if you find any other issues, appreciate the feedback.
  • Jon
    Well, this is part of the problem:

    pages = math.ceil(count / self.step)

    Should be:

    pages = math.ceil(float(count) / self.step)
  • Jon
    I tried this out. Pretty cool. Thanks a bunch for the contribution. However, it's not working well for lists resulting in a small numbers of pages, say 10 or less. And it never wants to show the last page number. Maybe I did something wrong.. I'll dig some more this weekend.
  • Brian,

    To your point, it may be a good idea to add configuration for the largest page shown but I think it would take away from the functionality.

    Typically when a user sorts results they are interested in the first few pages and the last few pages. Think of Digg.com, first few are most popular, last few are newest. Expand that to sort by alphabet, hits, velocity, number of comments, etc... the ends of the list are most interesting, not necessarily the middle.
  • dunno about that one...i am more interested in seeing a link to page 6, than i am for 1039...how about a parameter to the template tag to get rid of that.
  • Nick
    Very useful template tag! Thank you.

    Could you please post it into djangosnippets.org?
blog comments powered by Disqus