如何在 Django 模板语言中正确构建 for 循环

Posted

技术标签:

【中文标题】如何在 Django 模板语言中正确构建 for 循环【英文标题】:How to correctly structure a for loop in Django templating language 【发布时间】:2016-04-01 06:23:44 【问题描述】:

我在 Django 应用程序中有一个模板文件,该文件使用模板变量使用来自相应视图的数据。在视图的上下文中(在呈现页面时返回)我有一个名为 len_values 的变量,它存储循环结束范围的值。我目前的 for 循环结构是这样的:

 <ul class= 'list-group'>
     % for i in range(0,len_values) %
         <li  class = 'list-group-item' >
             Artist: form_artistSelect
             Location: venues.i.city, venues.i.region
             Venue: venues.i.name
             Date: dates.i
             tickets status: ticket_statuses.i<br>
             <a href = ticket_urls.i> ticket link </a>
             % if user.is_authenticated %
                 <button id = 'invite' type='button' class= 'btn btn-info btn-lg' data-toggle = 'modal' data-target='#myModal' venue=venues.i date=dates.i ticket_url=ticket_urls.i artist = form_artistSelect> Invite a friend </button>
                 <button id = 'save' type = 'button' class = 'btn btn-primary-outline'> Save Concert </button>
             % endif %
         </li>
     % endfor %
 </ul>

当我尝试这段代码时,我收到了错误:Could not parse the remainder: '(0,len_values)' from 'range(0,len_values)'

我已经看到 this old SO post 似乎正在解决类似的问题,但没有基于投票的结论性答案,并且提供的答案是旧的。

在 Django 模板格式中构造这个 for 循环的正确方法是什么

【问题讨论】:

如您在official docs 中所见,语法为% for var in iterable %。所以将range(len_values) 作为可迭代对象传递。请注意:传递值列表可能会更好,然后在模板循环中,您可以使用 forloop.counter 访问计数器 【参考方案1】:

正如文档清楚显示的那样,您根本无法在 Django 的模板语言中执行此操作。即使您使范围位工作,您的变量查找本身仍然无法工作:venues.i.city 中的i 始终被解释为文字名称“i”。

但无论如何您都不想这样做:即使在原始 Python 中也不应该这样做。你永远不会迭代 range(len(something)); 总是迭代事物本身。在这里也是如此:在您看来,您应该创建一个数据结构 - 比如说,一个字典列表 - 包含按项目分组的城市、地区、日期、状态等,然后在模板中遍历它:

data = [
    'city': "City 1", "region": "Region 1", "date": date_1, ....,
    ...
]

模板:

% for item in data %
    Location:  item.city ,  item.region 
    ...
% endfor %

【讨论】:

【参考方案2】:

正如您在official docs 中看到的那样,语法是

% for var in iterable %

因此,在视图中将上下文中的range(len_values) 作为可迭代对象传递。

请注意:传递值列表而不计算其长度会更好。

如果您需要每个项目的索引,在模板循环中,您可以使用forloop.counter 访问它(如果从零开始,请使用forloop.counter0),例如

% for item in data %
    This is item number  forloop.counter 
     item.city 
     item.region 
% endfor %

【讨论】:

【参考方案3】:

Django 模板标签不是 python 代码。你不能在这里只使用函数。但是你可以像在 python 中一样,使用 for 循环遍历一个可迭代对象。

第二个不起作用的是:

Location: venues.i.city, venues.i.region

我不会在这里被视为变量,而是作为场地中的字段名称。

正确的解决方案是:

 <ul class= 'list-group'>
     % for venue in venues %
         <li  class = 'list-group-item' >
             Artist: form_artistSelect
             Location: venue.city, venue.region
             Venue: venue.name
             Date: dates.i
            ... etc ...
         </li>
     % endfor %
 </ul>

但是有一个问题:您试图在多个列表中使用该变量。 Django 模板默认不允许这样做,所以这里有 3 个选项...

1。将列表加入一个您将迭代的列表。

这是首选解决方案,因为准备数据应该进入您的视野。

2。创建您自己的模板标签或过滤器

您可以使用一些自定义模板标签或过滤器从其他列表中提取数据。然后简单地遍历列表之一并将 forloop.counter 或 forloop.counter0 传递给您的自定义标签以提取特定数据。

3。使用肮脏的技巧

有一种方法可以绕过 django 模板的这个限制,但它真的很脏,你不应该使用它。这是一个例子:

<ul class= 'list-group'>
     % for venue in venues %% with index=forloop.counter0|stringformat:"i"|add:":" %% with date=dates|slice:index ticket_status=ticket_statuses|slice:index ticket_url=ticket_urls|slice:index %
         <li  class = 'list-group-item' >
             Artist: form_artistSelect
             Location: venue.city, venue.region
             Venue: venue.name
             Date: date.0
             tickets status: ticket_status.0<br>
             <a href = ticket_url.0> ticket link </a>
             % if user.is_authenticated %
                 <button id = 'invite' type='button' class= 'btn btn-info btn-lg' data-toggle = 'modal' data-target='#myModal' venue=venue date=date.0 ticket_url=ticket_url.0 artist = form_artistSelect> Invite a friend </button>
                 <button id = 'save' type = 'button' class = 'btn btn-primary-outline'> Save Concert </button>
             % endif %
         </li>
     % endwith %% endwith %% endfor %
 </ul>

【讨论】:

【参考方案4】:

Django 模板的设计使其语法尽可能简单。这个想法是,模板是由设计师而不是程序员来实现的。

在您的情况下,问题在于您不能简单地在 for 标记内添加诸如 range(0, len_values) 之类的 Python 代码。 for 标记需要一个迭代器,它可以由过滤器处理,但不是任意 Python 表达式。

您有不同的选项来迭代给定的次数:

最简单的方法是更改​​视图,因此您可以将数组本身传递给模板,而不是数组的 len(上下文中有 range(len_values),而不仅仅是 len_values)。 您可以创建自己的模板标签,它可能类似于内置的 for,但需要 len,而不是迭代器。 您可以使用 Jinja2,它在语法上与 Django 模板非常相似,但您在模板语法方面不受限制(不确定在这种情况下您的代码是否可以工作)。

但在你的情况下,我认为问题是另一个问题,在视图方面。与其让场地、日期、ticket_statuses 等的不同迭代器都具有相同的长度,不如只使用一个包含所有信息的迭代器。

所以,不要有这样的视图:

def my_view(request):
    context = 'venues': ['venue 1', 'venue 2', 'venue 3'],
               'dates': ['date 1', 'date 2', 'date 2'],
               'ticket_statuses': ['status 1', 'status 2', 'status 3']
    render(request, 'my_template.html', context)

你应该有类似的东西:

def my_view(request):
    context = 'tickets': [
                  'venue': 'venue 1', 'date': 'date 1', 'status': 'status 1',
                  'venue': 'venue 2', 'date': 'date 2', 'status': 'status 2',
                  'venue': 'venue 3', 'date': 'date 3', 'status': 'status 3']
    render(request, 'my_template.html', context)

然后,您的模板将很简单:

<ul class= 'list-group'>
     % for ticket in tickets  %
         <li  class = 'list-group-item' >
             Artist: form_artistSelect
             Location: ticket.venue.city, ticket.venue.region
             Venue: ticket.venue.name
             Date: ticket.date
             tickets status: ticket.status<br>
             <a href = ticket.url> ticket link </a>
             % if user.is_authenticated %
                 <button id = 'invite' type='button' class= 'btn btn-info btn-lg' data-toggle = 'modal' data-target='#myModal' venue=venues.i date=dates.i ticket_url=ticket_urls.i artist = form_artistSelect> Invite a friend </button>
                 <button id = 'save' type = 'button' class = 'btn btn-primary-outline'> Save Concert </button>
             % endif %
         </li>
     % endfor %
 </ul>

【讨论】:

感谢您的回答!我最初得到的 JSON 格式是this,其中每个对象没有像“票证”这样的名称。由于每个对象都没有命名,我还能继续这样做吗? 当然,那么你可能想要这样的东西:render(request, 'my_template.html', context='tickets': your_json)

以上是关于如何在 Django 模板语言中正确构建 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

Jinja / Django for 循环范围不起作用

Django 模板语言:使用带有 else 的 for 循环

在Django模板中使用for循环显示多个chart.js图表

如何通过变量/方法将Django模板代码注入模板?

在 Django 模板中使用 for 循环显示多个 chart.js 图表

图像未出现在 Django 模板 For Loop 中