agaskar.com

Another reason to love django: the regroup template tag.

If you've ever done any CMS work, you'll know that you'll often want to group a list of database records together by date or another properties. Django makes this easy with the regroup template tag. Read on to find out how it works.

For instance, you might want to have something like the following:

<div class="week"><h2>This Week</h2>
    <div class="articles">
        <div class="item">
            <span>Another reason to love django</span>
        </div>
        <div class="item">
            <span>admin page memory issue /
 WSOD with free tag taxonomies in Drupal</span>
        </div>
     </div>
</div>
<div class="week"><h2>Last Week</h2>
    <div class="articles">
        <div class="item">
            <span>Upgrade from Drupal 5.x to Drupal 6.x:
 The easy way. (if you've got root!).</span>
        </div>
    </div>
</div>

Previously, I've dealt with this problem in one of two methods, neither or which I have been satisfied with. The first method involves recursing through the data before spitting it out to the template to reorder it into arrays that are keyed by group. I don't like this because it involves stepping through the entire data result twice, once while we're re-indexing, and again while we're printing it to screen. If you're doing this while building a result array from a MySQL result object, it doesn't strike as terrible, since you're probably building an array regardless; still, it feels a little inflexible; it's difficult to see why you're reordering without the benefit of a corresponding template. The other method is to put this grouping logic into the template, which I've found can be very confusing (it often involves a conditional test on a loop variable or excessive logic statements) for designers working with you, and, more importantly, seems to violate the principle of keeping logic and display separate.

Django templates have a great built-in solution for this: {% regroup %} (and judging by this post, it seems like it'd be trivial to build a plugin for Smarty)

regroup takes a property of the element and splits your QuerySet into a series of groups based on a object property you specify. For example, if we had a weekGroup variable in our list, we could use this to group all our articles by week.

The upside of Django is that I can use the model defintion to create a calculated field that we can use to regroup by; resulting in a pretty elegant solution. No recursing through our results to add this var after we pull the data.

If we had an article model, we'd simply add something like:

def weekGroup(self):
    daysDelta=(datetime.now()-self.pub_date).days
    if (daysDelta)<=7:
        weekGroup='This Week'
    elif (daysDelta)<=14:
        weekGroup='Last Week'
    else:
       weekGroup=str(daysDelta//7)+' Weeks Ago'
   return weekGroup

Ok, we really should count from the saturday (day 6) of the week containing the article pub date, but this is just an example!

Then in the template we'd do something like this:

{% regroup articleList by groupWeek as groupedArticles %}

    {% for articles in groupedArticles %}
        <div class="week"><h2>{{ articles.grouper }}</h2>
            {% for article in articles.list %}
                <div class="item"><span>{{article.title}}</span></div>
            {% endfor %}
        </div>
    {% endfor %}

Is it fast? Well, I don't know -- I haven't checked. I do know that if it isn't, it sits in a single routine that can be easily optimized in future versions of django. It's easily understandable, django convention, and thus *maintainable*, and that's my major concern.

Read more about regroup in the official Django template documentation on the regroup tag.