【问题标题】:Iterating through two lists in Django templates遍历 Django 模板中的两个列表
【发布时间】:2011-01-25 19:55:19
【问题描述】:

我想在 django 模板中进行以下列表迭代:

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

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

django 代码:

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

尝试此操作时出现以下错误:

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

我该如何做到这一点?

【问题讨论】:

  • 一个棘手的用例是当您有一个master 列表['I', 'you', 'he']sublists = [['me' ,'you', 'him'], ['my', 'your', 'his'], ['mine', 'yours', 'his']]。如果您想将每个子列表与master 一起迭代,则必须在视图中压缩每个子列表。

标签: python django django-templates


【解决方案1】:

您可以在视图中使用zip

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

并在您的模板中使用

{% for item1, item2 in mylist %}

遍历两个列表。

这应该适用于所有版本的 Django。

【讨论】:

  • 不要使用“list”作为变量名,因为这会破坏内置的。
  • 检测到反模式!使用 jinja,不要混合你的视图和模板逻辑
  • 这是一个不错且快速的解决方案。请不要用你的 cmets 来迷惑别人,尤其是初学者。
【解决方案2】:

在views.py中:

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

在template.html中:

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

如果f 是从数据库返回的查询集,则通过{{f.required_attribute_name}} 访问它

【讨论】:

    【解决方案3】:

    这里修改了 {% for %} 模板标签,它允许一次迭代多个列表 izip-ing 之前:

    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)
    

    只需将其保存为模板标签库并将其导入您的模板。它将覆盖内置的 {% for %} 标签(不用担心它向后兼容)。

    示例用法:

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

    【讨论】:

      【解决方案4】:

      只需将 zip 定义为 template filter:

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

      然后,在您的模板中:

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

      【讨论】:

      • 完美解决方案!谢谢
      • 这是最好的解决方案,因为我不必更改上下文。谢谢。
      【解决方案5】:

      您可以在服务器端制作 moo 对象的 foo 对象属性。

      for f, b in zip(foo, bar):
          f.foosBar = b
      
      context = {
          "foo": foo
      }
      

      当第二个列表是第一个列表的属性时(通常是这种情况),这尤其干净。

      users = User.objects.all()
      for user in users:
          user.bestFriend = findBestFriendForUser(user)
      
      context = {
          "users": users
      }
      

      【讨论】:

        【解决方案6】:

        我构建了django-multiforloop 来解决这个问题。来自自述文件:

        安装了 django-multiforloop,渲染这个模板

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

        在这种情况下

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

        会输出

        one:two
        1:2
        carrot:orange
        

        【讨论】:

        • 很酷的应用程序......但我无法让它工作:/另外,我碰巧需要一些可以多次循环小数组的东西。
        【解决方案7】:

        可以这样做

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

        但您不能在for 结构中调用zip。您必须先将压缩列表存储在另一个变量中,然后对其进行迭代。

        【讨论】:

        • 语法 a, b 在 django 模板版本 0.96 中是不可能的,我猜是谷歌应用引擎使用的,因为当我尝试使用上述语法时出现错误。而是执行 {%for item in mylist%} 并使用 item.0 和 item.1。来自groups.google.com/group/django-users/browse_thread/thread/…
        • 好的。那么我的答案甚至可能是错误的。我只知道在 for 循环中可以进行元组拆包,并得出结论认为错误必须来自 zip 调用。不过没有测试它 - 抱歉。
        • 已修复。底线很简单:您不能在模板中进行任何计算;您必须在视图函数中进行 ALL 计算。此外,使用zip 是一个糟糕的选择;命名元组是一个更好的主意,因为它使模板更合理。
        • @Lott Care 用一个例子来详细说明。?没有得到你,因为我对 python 还很陌生。
        • @Abhi:由于您的问题没有详细信息,因此很难编造一个可能有用的示例。我可以随机猜测您要做什么。相反,首先使用 namedtuple 而不是 zip。如果您仍有问题 - 好吧 - 提出问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 2010-10-21
        • 2018-02-24
        • 2020-04-04
        • 2011-05-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多