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