creating an inline comma-separated list

3 views
Skip to first unread message

Eric Abrahamsen

unread,
Dec 8, 2007, 1:28:02 AM12/8/07
to Django users
I've just come up with what looks like a rather ugly solution for
certain problem, and I'm hoping someone might have comments for
improvement.

I've got a Book model, with a M2M relationship with an Author model,
ie possibly several authors per book. In my book template, I wanted to
use a for loop to produce output like so:

One author: "This book is by John Doe."
Two authors: "This book is by John Doe and Jane Doe."
Three or more authors: "This book is by John Doe, Jane Doe, Eustace
Tilley, [more] and Fred"

After much screwing around with for loops in the view and in the
template, I eventually decided to construct a mini-template using a
function in the views.py file. It looks like this (item is the book
object, and it has an author field):

======
def makeAuthChunk(item):
authCount = item.author.count()
if authCount == 1:
auth = item.author.all()[0]
authChunk = """<a href="%s">%s</a>""" %
(auth.get_absolute_url(), auth)
elif authCount == 2:
auth1 = item.author.all()[0]
auth2 = item.author.all()[1]
authChunk = """<a href="%s">%s</a> and <a href="%s">%s</a>"""
% (auth1.get_absolute_url(), auth1, auth2.get_absolute_url(), auth2)
else: # ie, more than 2 authors
firstAuth = item.author.order_by("id")[0]
lastAuth = item.author.order_by("-id")[0]
otherAuth =
item.author.all().exclude(pk=firstAuth.pk).exclude( pk=lastAuth.pk)
authChunk = """<a href="%s">%s</a>""" %
(firstAuth.get_absolute_url(), firstAuth)
for auth in otherAuth:
authChunk += """, <a href="%s">%s</a>""" %
(auth.get_absolute_url(), auth)
authChunk += """ and <a href="%s">%s</a>""" %
(lastAuth.get_absolute_url(), lastAuth)
return authChunk

=====

Hope the formatting survives. I have two questions:

1. Is there really no easier way to do this?
2. I'm up against the negative indexing problem in getting the last
author, and while the negative order_by seems okay, given that id is
unique, I'm wondering if there is a better way to accomplish that.

Any suggestions or alternative solutions would be much appreciated...

Thanks,
Eric

James Bennett

unread,
Dec 8, 2007, 1:51:45 AM12/8/07
to django...@googlegroups.com
On Dec 8, 2007 12:28 AM, Eric Abrahamsen <gir...@gmail.com> wrote:
> Hope the formatting survives. I have two questions:
>
> 1. Is there really no easier way to do this?
> 2. I'm up against the negative indexing problem in getting the last
> author, and while the negative order_by seems okay, given that id is
> unique, I'm wondering if there is a better way to accomplish that.
>
> Any suggestions or alternative solutions would be much appreciated...

On my blog, I have a many-to-many relation between entries and
categories, and in the sidebar of an entry I display the list of
categories. For example:

The entry at http://www.b-list.org/weblog/2007/dec/04/magic-tags/
belongs to one category, and so displays

"It's part of the category Django."

The entry at http://www.b-list.org/weblog/2007/feb/16/javascript-knowledge-gap/
is part of two categories, and so displays

"It's part of the categories JavaScript and Programming."

The entry at http://www.b-list.org/weblog/2007/jan/22/choosing-javascript-library/
is in three categories, and so displays

"It's part of the categories Frameworks, JavaScript and Programming."

The template snippet which does this is actually pretty simple:

It&#8217;s part of the categor{{
object.categories.count|pluralize:"y,ies" }} {% for category in
object.categories.all %}<a href="{{ category.get_absolute_url }}">{{
category.title }}</a>{% if forloop.last %}{% else %}{% ifequal
forloop.revcounter0 1 %} and {% else %}, {% endifequal %} {% endif
%}{% endfor %}.

In short:

1. Pluralize "category" appropriately to "categories" when there's
more than one category for the entry.
2. Loop through the categories.
3. Each time through, link to the category and show its title.
4. On the last trip through the loop, don't do anything special.
5. Otherwise, if it's the next-to-last time display a space followed
by the word "and" followed by a space.
6. Otherwise, display a comma followed by a space.

I don't like the serial comma (e.g., I prefer "Frameworks, JavaScript
and Programming" to "Frameworks, JavaScript, and Programming"), but
that'd be easy enough to do by changing step 5 to show "and," instead
of "and".
--
"Bureaucrat Conrad, you are technically correct -- the best kind of correct."

Eric Abrahamsen

unread,
Dec 8, 2007, 2:24:50 AM12/8/07
to Django users
> The template snippet which does this is actually pretty simple:
>
> It&#8217;s part of the categor{{
> object.categories.count|pluralize:"y,ies" }} {% for category in
> object.categories.all %}<a href="{{ category.get_absolute_url }}">{{
> category.title }}</a>{% if forloop.last %}{% else %}{% ifequal
> forloop.revcounter0 1 %} and {% else %}, {% endifequal %} {% endif
> %}{% endfor %}.

Of course! When I tried to do this in a forloop in the template I was
stuck thinking I had to include the whole author link thing in the
forloop.rev test, lord only knows why. This is an excellent solution,
and my karmic debt to you and the b-list continues to grow by the
day...

E
Reply all
Reply to author
Forward
0 new messages