web-dev-qa-db-fra.com

Itération à travers deux listes dans les modèles Django

Je veux faire l'itération de liste ci-dessous dans les modèles Django:

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%}

J'obtiens l'erreur ci-dessous quand j'essaie 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 puis-je accomplir ceci?

29
Abhi

Il est possible de faire

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

mais vous ne pouvez pas appeler Zip dans la structure for. Vous devrez d'abord stocker la liste compressée dans une autre variable, puis la parcourir par la suite.

17
Johannes Charra

Vous pouvez utiliser Zip à votre vue:

mylist = Zip(list1, list2)
return render(request, 'template.html', {'list': list, ... })

et dans votre template utiliser

{% for item1, item2 in mylist %}

parcourir les deux listes.

Cela devrait fonctionner avec toutes les versions de Django.

49
Mermoz

Définissez simplement Zip en tant que filtre template :

@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%}
24
Marco

J'ai construit Django-multifloroop pour résoudre ce problème. Du README:

Avec Django-multiforloop installé, restituer ce modèle

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

avec ce contexte

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

va sortir

one:two
1:2
carrot:orange
5
Gabriel Grant

Voici le {{for for}} templatetag modifié qui permet d'itérer plusieurs listes à la fois en les lisant auparavant:

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 de modèles et importez-le dans votre modèle. Elle remplacera la balise {% pour%} 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 %}
2
jaboja

Vous pouvez définir les propriétés des objets foo des objets moo côté serveur. 

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

context = {
    "foo": foo
}

Ceci est particulièrement net lorsque la deuxième liste est une propriété de la première (ce qui est généralement le cas). 

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

context = {
    "users": users
}
1
bbrame

Dans views.py:

foo = ['foo', 'bar']
moo = ['moo', 'loo']
zipped_list = Zip(foo,moo)
return render(request,"template.html",{"context":zipped_list}

Dans template.html:

{% for f,m in context%}
 {{f}}{{m}}
{% endfor %}

Si f est un ensemble de requêtes renvoyé de la base de données, accédez-y par {{f.required_attribute_name}}

0
Sm Srikanth