plantilla personalizada Tag
from django import template
register = template.Library()
def parse_tokens(parser, bits):
"""
Parse a tag bits (split tokens) and return a list on kwargs (from bits of the fu=bar) and a list of arguments.
"""
kwargs = {}
args = []
for bit in bits[1:]:
try:
try:
pair = bit.split('=')
kwargs[str(pair[0])] = parser.compile_filter(pair[1])
except IndexError:
args.append(parser.compile_filter(bit))
except TypeError:
raise template.TemplateSyntaxError('Bad argument "%s" for tag "%s"' % (bit, bits[0]))
return args, kwargs
class ZipLongestNode(template.Node):
"""
Zip multiple lists into one using the longest to determine the size
Usage: {% zip_longest list1 list2 <list3...> as items %}
"""
def __init__(self, *args, **kwargs):
self.lists = args
self.varname = kwargs['varname']
def render(self, context):
lists = [e.resolve(context) for e in self.lists]
if self.varname is not None:
context[self.varname] = [i for i in map(lambda *a: a, *lists)]
return ''
@register.tag
def zip_longest(parser, token):
bits = token.contents.split()
varname = None
if bits[-2] == 'as':
varname = bits[-1]
del bits[-2:]
else:
# raise exception
pass
args, kwargs = parse_tokens(parser, bits)
if varname:
kwargs['varname'] = varname
return ZipLongestNode(*args, **kwargs)
Uso:
{% zip_longest list1 list2 as items %}
Esto le permite pasar 2 o más listas de una etiqueta a continuación, iterar sobre la variable artículos. Si usas más de dos listas, entonces necesitarás repetirlo desafortunadamente. Sin embargo, con dos listas que he usado el primer y último filtros dentro del bucle de esta manera:
{% for item in items %}
{% with item|first as one %}
{% with item|last as two %}
<p>{{ one }}</p>
<p>{{ two }}</p>
{% endwith %}
{% endwith %}
{% endfor %}
Sin embargo, después de haber construido todo esto, podría ser mejor hacer esto en una vista!
itertools de Python
También debe considerar itertools de Python, que tiene el método izip_longest que toma dos o más listas. Devuelve las listas como una usando la lista más larga para determinar el tamaño (si desea concatenar a la lista más corta, no busque más allá de izip). Puede elegir con qué vaciar los valores vacíos utilizando la palabra clave fillvalue
, pero de manera predeterminada, esto es Ninguno.
Tanto izip_longest como izip devuelven un iterador en lugar de una lista, por lo que podría ver cierta ganancia de rendimiento en sitios más grandes.
Es importante tener en cuenta que izip_longest podría golpear el db un poco más de lo necesario dependiendo de cómo determine la longitud de cada lista (realizar un conteo() sería una llamada adicional al db). Sin embargo, no he podido probar esto de manera confiable y solo importaría una vez que tuvieras que escalar.
¡Gracias una tonelada! Solución perfecta. – demos
¿Cómo usaría la solución 2 en una plantilla? – webtweakers