python - fields - Iteración a través de dos listas en plantillas de Django
django forms widgets (6)
Aquí está el templatetag {% for%} modificado que permite iterar varias listas a la vez einculinándolas antes:
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)
Simplemente guárdelo como una biblioteca de plantillas e impórtelo en su plantilla. Anulará la etiqueta incorporada {% for%} (no se preocupe, es compatible con versiones anteriores).
Ejemplo de uso:
{% for a,b in foo, moo %}
{{ a }}
{{ b }}
{% endfor %}
Quiero hacer la iteración de la lista a continuación en las plantillas de django:
foo = [''foo'', ''bar''];
moo = [''moo'', ''loo''];
for (a, b) in zip(foo, moo):
print a, b
código django:
{%for a, b in zip(foo, moo)%}
{{a}}
{{b}}
{%endfor%}
Aparece el siguiente error cuando pruebo esto:
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
¿Cómo logro esto?
Es posible hacer
{% for ab in mylist %}
{{ab.0}}
{{ab.1}}
{% endfor %}
pero no puede hacer una llamada para zip
dentro de la estructura for
. Primero tendrá que almacenar la lista comprimida en otra variable y luego iterar sobre ella.
Puede hacer que las propiedades de objetos foo de los objetos moo en el lado del servidor.
for f, b in zip(foo, bar):
f.foosBar = b
context = {
"foo": foo
}
Esto es especialmente limpio cuando la segunda lista son propiedades de la primera (que generalmente es el caso).
users = User.objects.all()
for user in users:
user.bestFriend = findBestFriendForUser(user)
context = {
"users": users
}
Puedes usar zip
en tu vista:
mylist = zip(list1, list2)
return render_to_response(''template.html'', {''list'': list, ... })
y en el uso de tu plantilla
{% for item1, item2 in mylist %}
para iterar a través de ambas listas.
Esto debería funcionar con todas las versiones de Django.
Simplemente defina zip como un filtro de plantilla :
@register.filter(name=''zip'')
def zip_lists(a, b):
return zip(a, b)
Luego, en tu plantilla:
{%for a, b in first_list|zip:second_list %}
{{a}}
{{b}}
{%endfor%}
django-multiforloop para resolver este problema. Del LÉAME:
Con django-multiforloop instalado, renderizando esta plantilla
{% for x in x_list; y in y_list %}
{{ x }}:{{ y }}
{% endfor %}
con este contexto
context = {
"x_list": (''one'', 1, ''carrot''),
"y_list": (''two'', 2, ''orange'')
}
saldrá
one:two
1:2
carrot:orange