The Fluther Blog

Back to Fluther

What is Fluther?

Fluther is a free Q&A collective that specializes in getting
fast answers from the right people. Check it out!

“How’d they do that?” Pagination

8:58 am

In this week’s installment, we show you something simple but important: pagination. If you’ve ever seen those little numbers at the bottom of a Google Search, then you’re familiar with a paginated list:

Now we’ll show you how we made a reusable paginator for different parts of Fluther.

First we had to decide how it should look, and which side would be for “Newer” and “Older”. This was surprisingly non-obvious. We thought carefully about our UI, discussed this great article by Ivan Kanevski, and came up with something we liked.

Now, how did we build it?

We wanted it to be simple and reusable, so we made a Django inclusion tag that gets called like this:

<p class="nav">
{% render_pagination %}
</p>

Inside the inclusion tag is some tricky arithmetic, but the newest version of Django provides a Paginator object, so we’ll spare you our implementation. Basically, it takes the range of pages and your current location in the list and tells you which numbers should show up–nothing too fancy.

Now the template itself is pretty straightforward:

{% if is_paginated %}
{% if has_previous %}
<a href="{{linkprefix}}page={{previous}}#browse" id="newer">&#0171; Newer</a>
{% else %}
<span id="newer">&#0171; Newer</span>
{% endif %}
<span class="pagination">
{% if more_new %}
<a href="{{linkprefix}}page=1#browse" class="first">1</a>...
{% endif %}
{% for i in page_range %}{% spaceless %}
{% ifequal i page %}
<span class="current">{{i}}</span>
{% else %}
<a href="{{linkprefix}}page={{i}}#browse"
{% if forloop.first %}class='first'{% endif %}{% if forloop.last%}class="last"{% endif %}
>{{i}}</a>
{% endifequal %}
{% endspaceless %}{% endfor %}{% if more_old %}...{% endif %}
</span>
{% if has_next %}
<a href="{{linkprefix}}page={{next}}#browse" id="older">Older &#0187;</a>
{% else %}
<span id="older">Older &#0187;</span>
{% endif %}
{% endif %}

A few things to note here:

  1. linkprefix. This allows us to use the paginator on different urls without any modification. Nifty.
  2. We’re assuming the #browse anchor, which isn’t the most flexible solution, but it’s sufficient for now. Eventually we’ll factor this out to be a variable.

You may be wondering, how does this actually display multiple pages? How does it all come together? Here’s where Django is a champ. The secret is using django.views.generic.list_detail.object_list. It handles everything for you. You just have to name the list in your template “yourobject_list” and render with object_list. For example, here’s how we rendered the activity page before pagination:

    return render_to_response('v2/activity.html', {'act_list':new_act,
'bookmark_list':all_act,
'account_title':"Activity You’re Following"
}, context_instance=RequestContext(request))

and here’s how we render it with pagination:

    return object_list(request, queryset=all_act, paginate_by=25,
template_object_name="activity",
template_name="v2/activity.html",
extra_context={
'new_list':new_act,
'pagelinkprefix':"/activity/?",
'account_title':"Activity You’re Following"
})

Presto! Just like that, you’re in pagination heaven:

Any other mysteries of Fluther you’d like explained? Let us know and our secrets could be yours.