2010-03-10 5 views
25

Je veux faire l'itération de la liste ci-dessous dans les modèles de django:Itère deux listes dans Django templates

foo = ['foo', 'bar']; 
moo = ['moo', 'loo']; 

for (a, b) in zip(foo, moo): 
    print a, b 

Code django:

{%for a, b in zip(foo, moo)%} 
    {{a}} 
    {{b}} 
{%endfor%} 

Je reçois l'erreur ci-dessous lorsque je tente ceci:

File "/base/python_lib/versions/third_party/django-0.96/django/template/defaulttags.py", line 538, in do_for 
    raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents 

Comment accomplir ceci?

+0

Un cas d'utilisation délicate est quand vous avez une liste 'de master'' [ 'I', 'vous', 'il'] 'et' sous-listes = [[ 'moi' , 'vous', 'lui'], ['mon', 'votre', 'son'], ['mien', 'le vôtre', 'son']]]. Si vous voulez itérer chaque sous-liste avec 'master', vous devez zipper chacun d'entre eux dans la vue. – akaihola

Répondre

12

Il est possible de faire

{% for ab in mylist %} 
    {{ab.0}} 
    {{ab.1}} 
{% endfor %} 

mais vous ne pouvez pas faire un appel à zip dans la structure for. Vous devrez d'abord stocker la liste compressée dans une autre variable, puis la parcourir.

+1

syntaxe a, b n'est pas possible dans la version du modèle django 0.96 que google app engine utilise, je suppose, car j'obtiens une erreur lorsque j'essaie d'utiliser la syntaxe ci-dessus. À la place, faites {% pour l'élément dans mylist%} et utilisez item.0 et item.1. Vous l'avez trouvé sur http://groups.google.com/group/django-users/browse_thread/thread/9160209ccfa94a30?pli=1 – Abhi

+0

Ok. Alors ma réponse pourrait même être fausse après tout. Je savais juste que le déballage de tuple est possible dans les boucles pour conclure que l'erreur doit provenir de l'appel 'zip'. Je n'ai pas testé, cependant - désolé. –

+0

Fixé. Bottom line est simple: vous ne pouvez pas faire de calcul dans le modèle; vous devez faire * TOUS * vos calculs dans les fonctions de vue. En outre, l'utilisation de 'zip 'est un mauvais choix; Un namedtuple est une bien meilleure idée car cela rend le template plus sensé. –

45

Vous pouvez utiliser un zip dans votre vue:

list = zip(list1, list2) 
return render_to_response('template.html', {'list': list, ... }) 

et dans votre modèle

{% for item1, item2 in list %} 

à itérer les deux listes.

Cela devrait fonctionner avec toutes les versions de Django

+3

N'utilisez pas "list" comme nom de variable, car cela claque le built-in. –

+0

Anti-pattern détecté! Utilisez jinja et ne mélangez pas votre logique d'affichage et de modèle – MrKsn

5

Je django-multiforloop construit pour résoudre ce problème. De l'README:

Avec django-multiforloop installé, ce qui rend ce modèle

{% for x in x_list; y in y_list %} 
    {{ x }}:{{ y }} 
{% endfor %} 

ce contexte

context = { 
    "x_list": ('one', 1, 'carrot'), 
    "y_list": ('two', 2, 'orange') 
} 

Affichera

one:two 
1:2 
carrot:orange 
+0

Cool app .. mais je ne pouvais pas le faire fonctionner:/ Aussi, j'ai besoin de quelque chose qui va faire une boucle sur le petit tableau plusieurs fois. – Thumbz

1

Vous pouvez faire les foo propriétés des objets des objets moo du côté serveur.

for f, b in zip(foo, bar): 
    f.foosBar = b 

context = { 
    "foo": foo 
} 

Ceci est particulièrement propre lorsque la deuxième liste sont des propriétés de la première (qui est généralement le cas).

users = User.objects.all() 
for user in users: 
    user.bestFriend = findBestFriendForUser(user) 

context = { 
    "users": users 
} 
20

définissent simplement zip comme template filter:

@register.filter(name='zip') 
def zip_lists(a, b): 
    return zip(a, b) 

Ensuite, dans votre modèle:

{%for a, b in first_list|zip:second_list %} 
    {{a}} 
    {{b}} 
{%endfor%} 
+0

cool réponse thx – clime

1

Voici modifié {% for%} templatetag qui itération permet plusieurs listes à la fois izip-les ing avant:

import re 

from itertools import izip 
from django import template 
from django.template.base import TemplateSyntaxError 
from django.template.defaulttags import ForNode 

register = template.Library() 


class ZipExpression(object): 
    def __init__(self, var): 
     self.var = var 

    def resolve(self, *args, **kwargs): 
     return izip(*(
      f.resolve(*args, **kwargs) for f in self.var 
     )) 


@register.tag('for') 
def do_for(parser, token): 
    """ 
    For tag with ziping multiple iterables. 
    """ 
    bits = token.contents.split() 
    if len(bits) < 4: 
     raise TemplateSyntaxError("'foreach' statements should have at least" 
            " four words: %s" % token.contents) 

    is_reversed = False 
    try: 
     in_index = bits.index('in') 
     sequence = bits[in_index+1:] 
     if sequence[-1] == 'reversed': 
      is_reversed = True 
      sequence.pop() 
     if not sequence or 'in' in sequence: 
      raise ValueError 
     sequence = re.split(r' *, *', ' '.join(sequence)) 
    except ValueError: 
     raise TemplateSyntaxError(
      "'foreach' statements should use the format" 
      " 'foreach a,b,(...) in x,y,(...)': %s" % token.contents) 

    loopvars = re.split(r' *, *', ' '.join(bits[1:in_index])) 
    for var in loopvars: 
     if not var or ' ' in var: 
      raise TemplateSyntaxError("'foreach' tag received an invalid" 
             " argumewnt: %s" % token.contents) 

    if len(sequence) > 1: 
     sequence = ZipExpression(map(parser.compile_filter, sequence)) 
    else: 
     sequence = parser.compile_filter(sequence[0]) 

    nodelist_loop = parser.parse(('empty', 'endfor',)) 
    token = parser.next_token() 
    if token.contents == 'empty': 
     nodelist_empty = parser.parse(('endfor',)) 
     parser.delete_first_token() 
    else: 
     nodelist_empty = None 
    return ForNode(
     loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty) 

Enregistrez-le simplement en tant que bibliothèque templatetag et importez-le dans votre modèle. Il remplacera la balise {% for%} intégrée (ne vous inquiétez pas, elle est rétrocompatible avec elle).

Exemple d'utilisation:

{% for a,b in foo, moo %} 
    {{ a }} 
    {{ b }} 
{% endfor %} 
Questions connexes