简体   繁体   English

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

[英]How to correctly structure a for loop in Django templating language

I have a template file in a Django app that is using data from the corresponding view using template variables. 我在Django应用程序中有一个模板文件,该文件正在使用模板变量从相应视图中获取数据。 In the context of the view (which is returned as the page is rendered) I have a variable called len_values that stores the value of the end range of the loop. 在视图的上下文中(在渲染页面时返回),我有一个名为len_values的变量,该变量存储循环的结束范围的值。 I currently have the for loop structured with this syntax: 我目前有使用以下语法构造的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>

When I tried this code, I was getting the error: Could not parse the remainder: '(0,len_values)' from 'range(0,len_values)' 当我尝试此代码时,出现错误: Could not parse the remainder: '(0,len_values)' from 'range(0,len_values)'

I've seen this old SO post which seems to be solving a similar issue but there isn't a conclusive answer based on the upvotes and the answers provided are old. 我看过这则旧的SO帖子似乎正在解决类似的问题,但是根据投票结果并没有一个定论的答案,而且所提供的答案也很旧。

What would be the proper way to structure this for loop in the Django templating format 用Django模板格式构造for循环的正确方法是什么

You simply can't do this in Django's template language, as the documentation clearly shows. 正如文档清楚显示的那样,您根本无法使用Django的模板语言来执行此操作。 Even if you got the range bit working, your variable lookups themselves still wouldn't work: the i in venues.i.city is always interpreted as the literal name "i". 即使您使范围位起作用,您自己的变量查找本身也仍然不起作用: venues.i.cityi始终被解释为文字名称“ i”。

But you don't want to do it anyway: you shouldn't do it even in raw Python. 但是您还是不想这样做:即使在原始Python中也不应该这样做。 You would never iterate over range(len(something)); 您永远不会在范围上迭代(len(something)); always iterate over the thing itself. 总是对事物本身进行迭代。 It's just as true here: in your view, you should create a data structure - say, a list of dicts - containing the cities, regions, dates, statuses etc grouped per item, and then iterate through that in your template: 在这里也是如此:在您看来,您应该创建一个数据结构(例如字典列表),其中包含按项目分组的城市,地区,日期,状态等,然后在模板中进行迭代:

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

template: 模板:

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

Django templates were designed so it's syntax is as simple as possible. Django模板的设计使其语法尽可能简单。 The idea is that the templates were implemented by designers more than by programmers. 这个想法是模板由设计师而不是程序员来实现。

In your case, the problem is that you can't simply add Python code like range(0, len_values) inside the for tag. 在您的情况下,问题在于您不能简单地在for标记内添加诸如range(0,len_values)之类的Python代码。 The for tag expects an iterator, which may be processed by a filter, but not an arbitrary Python expression. for标签需要一个迭代器,该迭代器可以由过滤器处理,但不能由任意Python表达式处理。

You have different options to to iterate a given number of times: 您可以使用不同的选项来迭代给定的次数:

  • The easiest is to change your view, so instead of the len of the array, you pass the array itself to the template (you have the range(len_values) in the context, instead of just the len_values). 最简单的方法就是更改视图,因此将数组本身传递给模板(而不是len_values),而不是将数组本身传递给模板(在上下文中具有range(len_values))。
  • You can create your own template tag, which could be similar to the builtin for, but expecting the len, instead of an iterator. 您可以创建自己的模板标签,该标签可能类似于内置标签,但需要len而不是迭代器。
  • You can use Jinja2, which is very similar in syntax to Django templates, but you are not so limited in the template syntax (not sure if in this case your code would work). 您可以使用Jinja2,它的语法与Django模板非常相似,但是您对模板语法的限制不是很有限(不确定在这种情况下您的代码是否可以工作)。

But in your case, I think the problem is another one, in the view side. 但是从您的角度来看,我认为问题是另一个问题。 Instead of having different iterators for venues, dates, ticket_statuses, etc. all with the same length, you should have just one with all the information. 与其为场所,日期,ticket_statuses等提供不同的迭代器,而且它们具有相同的长度,而应该只包含一个包含所有信息的迭代器。

So, instead of having a view like: 因此,与其像这样查看:

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)

you should have something like: 您应该具有以下内容:

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)

Then, your template will be as simple as: 然后,您的模板将非常简单:

<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>

As you can see in the official docs the syntax is 如您在官方文档中所见,语法为

{% for var in iterable %}

So in the view pass range(len_values) in the context as the iterable. 因此,在视图range(len_values)在上下文中视为可迭代的。

Mind you: you would be much better off passing the list of values, without calculating its length. 请注意:传递值列表而不计算长度会更好。

In case you need the index of each item, in the template loop you can access it with forloop.counter (use forloop.counter0 if starting from zero), eg 如果需要每个项目的索引,则可以在模板循环中使用forloop.counter (如果从零开始,则使用forloop.counter0 )对其进行访问,例如

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

Django template tags are not python code. Django模板标签不是python代码。 You can't just use functions here. 您不能只在这里使用函数。 But you can, as in python, iterate through an iterable using for loop. 但是您可以像在python中一样,通过for循环来迭代。

Second thing that won't work is: 第二件事行不通的是:

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

I won't be treated as variable here, but as name of field in venues. 在这里,我将不会被视为变量,而是会场中的字段名称。

Proper solution will be: 正确的解决方案是:

 <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>

But there is problem: you're trying to use that variable in more than one list. 但是有一个问题:您试图在多个列表中使用该变量。 Django templates won't allow this by default, so you have 3 options here... Django模板默认情况下不允许这样做,因此这里有3个选项...

1. join lists into one list that you will iterate through. 1.将列表加入一个要迭代的列表中。

This is preferred solution, because preparing your data should go into your view. 这是首选的解决方案,因为准备数据应该进入您的视图。

2. create your own template tag(s) or filter(s) 2.创建自己的模板标签或过滤器

You can extract data from other lists using some custom template tag(s) or filter(s). 您可以使用一些自定义模板标签或过滤器从其他列表中提取数据。 Then simply iterate through one of lists and pass forloop.counter or forloop.counter0 to your custom tag to extract particular data. 然后,只需遍历列表之一,并将forloop.counter或forloop.counter0传递到您的自定义标记即可提取特定数据。

3. use dirty hack 3.使用肮脏的hack

There is one way to go around this limitation of django templates, but it is really dirty and you shouldn't use it. 有种方法可以解决django模板的这种限制,但是它确实很脏,您不应该使用它。 Here is example: 这是示例:

<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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM