suggestion for a slightly extended {% for ... in ... %} tag

3 views
Skip to first unread message

Arnaud Delobelle

unread,
Feb 17, 2007, 12:29:49 PM2/17/07
to Django developers
Hi everyone,

I have been using django with great pleasure and there is a slight
extension to the {% for ... in ... %} default tag I would like to
propose.

I find it useful to have the {% for %} tag syntax extended so that one
can write:
{% for a,b in L %} meaning the obvious thing

Also (maybe more contentious):
{% for i in L1,L2 %} meaning what one would write in python as:
for i in zip(L1,L2)

It is actually a trivial patch to the original ForNode and do_for in
django.template.defaulttags.py to extend {% for ... %} so that it
would accept anything of the form
{% for a_1,a_2,...,a_k in L %} (implying that L is a list of k-uples)
or
{% for a in L1,L2,...,L_n %} (then at each iteration, a will be an n-
uple)
or
{% for a_1,a_2,...a_k in L_1,L_2,...,L_k %} (where each a_i iterates
over L_i in 'parallel')

Provided that no space is allowed between commas and variables (i.e.
a,b NOT a, b) the modification is very simple (only 4 or 5 lines to be
changed). But I have noticed this sort of restriction is already in
place for other tags such as {% cycle ... %} so it would not be an
issue.

This new 'for' does not actually process data, just rearranges them so
that they correspond better to the desired layout. This would allow
transfering some such manipulations from views to templates where I
feel they belong more naturally.

If there is an interest for this I can provide the patch and follow
what procedure is customary for django (which I am not familiar with
yet ;)

--
Arnaud

oggie rob

unread,
Feb 17, 2007, 4:48:30 PM2/17/07
to Django developers
> I find it useful to have the {% for %} tag syntax extended so that one can write:
> {% for a,b in L %} meaning the obvious thing

You can do this. a,b return type is implicitly (a,b) in Python. In
other words:
{% for x in L %}
{{ x.0 }}
{{ x.1 }}
{% endfor %}

> Also (maybe more contentious):
> {% for i in L1,L2 %} meaning what one would write in python as:
> for i in zip(L1,L2)

This is starting to look like more logic in the template than you
would normally use. I think the idea of the {% for %} tag has been
(and should be) to be able to easily cycle through collections of data
you've already set up. Most of the time you don't have to manipulate
them in any way other than how you've collected the data in the first
place. If you *DO* need to manipulate them, you should use views, not
templates.

You can also write your own tag to do the same - however, I still
think the work should be done in the view.

My .02
-rob

Honza Král

unread,
Feb 17, 2007, 4:58:59 PM2/17/07
to django-d...@googlegroups.com
On 2/17/07, oggie rob <oz.rob...@gmail.com> wrote:
>
> > I find it useful to have the {% for %} tag syntax extended so that one can write:
> > {% for a,b in L %} meaning the obvious thing

+1

>
> You can do this. a,b return type is implicitly (a,b) in Python. In
> other words:
> {% for x in L %}
> {{ x.0 }}
> {{ x.1 }}
> {% endfor %}

well yes, but you have to admit that
{% for name, description in list %}
{{ name }} - {{ description }}
{% endfor %}
looks way better ;)
just some syntax sugar, but I think it's actually useful

>
> > Also (maybe more contentious):
> > {% for i in L1,L2 %} meaning what one would write in python as:
> > for i in zip(L1,L2)

+0 - I like this feature, but I didn't know when I first looked at it
whether its a zip() or concatenation, that should be clear

>
> This is starting to look like more logic in the template than you
> would normally use. I think the idea of the {% for %} tag has been
> (and should be) to be able to easily cycle through collections of data
> you've already set up. Most of the time you don't have to manipulate
> them in any way other than how you've collected the data in the first
> place. If you *DO* need to manipulate them, you should use views, not
> templates.
>
> You can also write your own tag to do the same - however, I still
> think the work should be done in the view.

well, I can see a use case for this - if you want to iterate over
multiple lists at a time... for example if you want to render
something to columns rather that rows in a table, that's a change that
shouldn't require a change to the view function, yet it is near
impossible to achieve withou custom filters or tags...

>
> My .02

and mine ;)

> -rob
>
>
> >
>


--
Honza Král
E-Mail: Honza...@gmail.com
ICQ#: 107471613
Phone: +420 606 678585

Todd O'Bryan

unread,
Feb 17, 2007, 5:29:14 PM2/17/07
to django-d...@googlegroups.com

On Feb 17, 2007, at 4:58 PM, Honza Král wrote:

> On 2/17/07, oggie rob <oz.rob...@gmail.com> wrote:
>>
>>> I find it useful to have the {% for %} tag syntax extended so
>>> that one can write:
>>> {% for a,b in L %} meaning the obvious thing
>
> +1
>
>>
>> You can do this. a,b return type is implicitly (a,b) in Python. In
>> other words:
>> {% for x in L %}
>> {{ x.0 }}
>> {{ x.1 }}
>> {% endfor %}
>
> well yes, but you have to admit that
> {% for name, description in list %}
> {{ name }} - {{ description }}
> {% endfor %}
> looks way better ;)
> just some syntax sugar, but I think it's actually useful

If you change the tuples into dictionaries in the view, you can get
the same kind of clarity (and that's what I find myself doing quite a
bit, actually):

{% for x in L %}

{{ x.name }}
{{ x.description }}
{% endfor %}

Todd

Arnaud Delobelle

unread,
Feb 17, 2007, 5:33:32 PM2/17/07
to Django developers

On Feb 17, 9:48 pm, "oggie rob" <oz.robhar...@gmail.com> wrote:
> > I find it useful to have the {% for %} tag syntax extended so that one can write:
> > {% for a,b in L %} meaning the obvious thing
>
> You can do this. a,b return type is implicitly (a,b) in Python. In
> other words:
> {% for x in L %}
> {{ x.0 }}
> {{ x.1 }}
> {% endfor %}

You can do this of course but it does not help to make sense of the
template. E.g.

{% for key,value in L %}
{{ key }}->{{ value }}
{% endfor %}

looks neater than

{% for key_value in L %}
{{ key_value.0 }}->{{ key_value.1 }}
{% endfor %}

[...]


> This is starting to look like more logic in the template than you
> would normally use. I think the idea of the {% for %} tag has been
> (and should be) to be able to easily cycle through collections of data
> you've already set up. Most of the time you don't have to manipulate
> them in any way other than how you've collected the data in the first
> place. If you *DO* need to manipulate them, you should use views, not
> templates.

Following this argument there is no need for {% for ... in L reversed
%} as L can be reversed in the view :)

Here is an example:

IMHO which way round a table is displayed shouldn't really be the
concern of the view but rather of the template. Due to the nature of
HTML (where a table essentially is a list of rows) the current form of
{% for ... %} forces this *layout* decision to take place in the view
unless one writes a custom tag.

Of course this is a more general problem that my suggestion does not
solve completely but it has helped me in some cases to move layout
decisions from views to templates.

--
Arnaud

Arnaud Delobelle

unread,
Feb 17, 2007, 5:39:08 PM2/17/07
to Django developers
[...]

> If you change the tuples into dictionaries in the view, you can get
> the same kind of clarity (and that's what I find myself doing quite a
> bit, actually):
>
> {% for x in L %}
> {{ x.name }}
> {{ x.description }}
> {% endfor %}
>

Yes I found myself doing that a lot too until I thought I'd done it
enough times :)
This is one of the motivations for me to change this : to get clarity
in the template without extra code in the view (code which is only
shuffling around data structures to make them fit with the {% for ...
%} tag

> Todd

--
Arnaud

oggie rob

unread,
Feb 17, 2007, 5:56:26 PM2/17/07
to Django developers
> You can do this of course but it does not help to make sense of the
> template. E.g.
>
> {% for key,value in L %}
> {{ key }}->{{ value }}
> {% endfor %}

Yes, however you're looking at the case of only two variables. Think
of how unmanageable it will start to look like this:
{% for name,description,value,href in L %}...

It is better to get a dict back from the view instead of creating one
in the template.

-rob

Rubic

unread,
Feb 17, 2007, 6:35:06 PM2/17/07
to Django developers
On Feb 17, 11:29 am, "Arnaud Delobelle" <arno...@googlemail.com>
wrote:

> I find it useful to have the {% for %} tag syntax extended so that one
> can write:
> {% for a,b in L %} meaning the obvious thing

+1 from me, too. Nicer than the current idiom.

--
Jeff Bauer
Rubicon, Inc.

Gary Wilson

unread,
Feb 17, 2007, 7:39:33 PM2/17/07
to Django developers
On Feb 17, 3:58 pm, "Honza Král" <honza.k...@gmail.com> wrote:

> On 2/17/07, oggie rob <oz.robhar...@gmail.com> wrote:
> > > I find it useful to have the {% for %} tag syntax extended so that one can write:
> > > {% for a,b in L %} meaning the obvious thing
>
> +1

+1
The {{ x.0 }} syntax is simply ugly and unreadable.

> > > Also (maybe more contentious):
> > > {% for i in L1,L2 %} meaning what one would write in python as:
> > > for i in zip(L1,L2)
>
> +0 - I like this feature, but I didn't know when I first looked at it
> whether its a zip() or concatenation, that should be clear

+0
For the same reason Honza mentioned. I would be ok with introducing
"zip" in there though:
{% for i in zip L1,L2 %}

limodou

unread,
Feb 18, 2007, 12:02:38 AM2/18/07
to django-d...@googlegroups.com
> I find it useful to have the {% for %} tag syntax extended so that one can write:
> {% for a,b in L %} meaning the obvious thing

+1

> Also (maybe more contentious):


> {% for i in L1,L2 %} meaning what one would write in python as:
> for i in zip(L1,L2)

+0


--
I like python!
UliPad <<The Python Editor>>: http://wiki.woodpecker.org.cn/moin/UliPad
My Blog: http://www.donews.net/limodou

Russell Keith-Magee

unread,
Feb 18, 2007, 1:54:20 AM2/18/07
to django-d...@googlegroups.com
On 2/18/07, oggie rob <oz.rob...@gmail.com> wrote:
>
> > I find it useful to have the {% for %} tag syntax extended so that one can write:
> > {% for a,b in L %} meaning the obvious thing

+1.

> > Also (maybe more contentious):
> > {% for i in L1,L2 %} meaning what one would write in python as:
> > for i in zip(L1,L2)

Most people seem to be +0 on this, but I'm very much -1.

Firstly, the proposed syntax reads to me as 'iterate over concatenated
list', not 'iterate over zipped list'

Secondly, remember that one of the audiences of the template language
is designers with no programming experience. IMHO, adding operational
modes to template tags in this way only serves to complicate what
needs to be a simple interface.

However - I acknowledge the use case: Two lists, n items long, that
are from independent sources. I think a separate template tag in the
same vein as {% regroup %} would be a better approach.

{% zip lista listb as combined %}
{% for a,b in combined %}
...

This would be easier to explain, unambiguous, and doesn't pollute the
simplicity of the for tag.

Yours,
Russ Magee %-)

Arnaud Delobelle

unread,
Feb 18, 2007, 4:10:18 AM2/18/07
to Django developers

On Feb 18, 6:54 am, "Russell Keith-Magee" <freakboy3...@gmail.com>
wrote:
[...]


> > > Also (maybe more contentious):
> > > {% for i in L1,L2 %} meaning what one would write in python as:
> > > for i in zip(L1,L2)
>
> Most people seem to be +0 on this, but I'm very much -1.
>
> Firstly, the proposed syntax reads to me as 'iterate over concatenated
> list', not 'iterate over zipped list'

This is what people have remarked on and I agree.

> Secondly, remember that one of the audiences of the template language
> is designers with no programming experience. IMHO, adding operational
> modes to template tags in this way only serves to complicate what
> needs to be a simple interface.

I find this argument powerful. However I am still willing to defend
the idea a bit :)

> However - I acknowledge the use case: Two lists, n items long, that
> are from independent sources. I think a separate template tag in the
> same vein as {% regroup %} would be a better approach.
>
> {% zip lista listb as combined %}
> {% for a,b in combined %}

This is definitely much better than my initial suggestion.
OTOH we could also add an optional 'zipped' keyword at the end (just
like 'reversed'):

{% for a,b in A,B zipped %} would be easier to interpret IMHO

To recap I now put forward the following, given that x can be a
variable or a comma-separated list of variables (i.e. a or a,b or
a,b,c ...)

1. {% for x in L %}
meaning: for x in L

example: {% for a,b in L %} to extract values from a list of pairs

2. {% for x in A,B[,C...] zipped %}
meaning: for x in zip(A,B[,C...])

example: {% for a,b in A,B zipped %} to extract values from 2 lists in
parallel
This idiom looks clear to me. However I am not a non-programmer ;)

3. {% for x in L zipped %}
meaning: for x in zip(*L)

example: given that table = [ row1, row2, ... ], each row being a
sequence of items.
{% for col in table zipped %} to iterate over the columns of 'table'
rather than the rows.

If people think 1. is good but 2. and 3. are confusing I will be glad
to submit a patch for 1. only. As someone pointed out I can always
have my own custom tags.

then to obtain the same functionality as 2. and 3. one could have as
suggested by Russell above:

2'. {% zip A,B[, C] as ABC %}

3', {% zip table as transposed %}

--
Arnaud

SmileyChris

unread,
Feb 18, 2007, 4:59:09 PM2/18/07
to Django developers

On Feb 18, 10:10 pm, "Arnaud Delobelle" <arno...@googlemail.com>
wrote:


> > However - I acknowledge the use case: Two lists, n items long, that
> > are from independent sources. I think a separate template tag in the
> > same vein as {% regroup %} would be a better approach.
>
> > {% zip lista listb as combined %}
> > {% for a,b in combined %}

I prefer using filters for this sort of thing:
{% for a,b in lista|zip:listb %}
{% endfor %}

Regarding the main part of this post (unpacking of lists in a for
tag), I'm surprised no-one has created a ticket yet!
Here it is now: http://code.djangoproject.com/ticket/3523

Honza Král

unread,
Feb 18, 2007, 5:35:09 PM2/18/07
to django-d...@googlegroups.com

I added a simple patch for that... it definitely needs improvement -
!!!! it will break current templates if it remains as is !!!! (a
single loopvar must be handled as a special case - simple change) and
it's not very verbose in it's error messages...

but assuming you treat it nicely, could you test it and verify it's
the thing you wanted? I will the polish the thing up a bit.

thanks

Reply all
Reply to author
Forward
0 new messages