Django模板嵌套循环:基于外层计数器访问内层列表索引
在Django模板中根据外层循环索引访问内层列表元素
问题背景
需要实现类似普通Python中通过外层循环索引访问对应内层列表的逻辑:
parent_list = ['One','Two','Three'] child_list = ['A','B','C'] for idx, item in enumerate(parent_list): for child_item in child_list[idx]: print(str(idx), item, child_item)
但在Django模板中尝试用with和forloop.counter0保存外层索引时,无法通过royalty_items.outer_counter的方式动态访问对应索引的元素,硬写固定索引(如royalty_items.0)只会重复显示同一组内容。
现有代码情况
模板代码(尝试失败版本)
{% for item in payout_items %} {% with forloop.counter0 as outer_counter %} <h2>{{ item.market_place }} on {{ item.entry_date }}: {% for item in royalty_items.outer_counter %} <tr> <th scope="row">{{ item.id }}</th> <td>{{ item.entry_date }}</td> <td>{{ item.market_place }}</td> </tr> {% endfor %} {% endwith %} {% endfor %}
视图层代码
视图中已准备好对应的两个列表:
detail_object = AccountingPeriod.objects.get(pk=detail_id) payout_items = Payout.objects.filter(sales_period_start__range=[detail_object.period_start, detail_object.period_end]) royalty_items = [] for payout in payout_items: temp = Royalty.objects.filter(entry_date__range=[payout.sales_period_start, payout.sales_period_end]).filter(market_place=payout.market_place) print(str(payout.sales_period_start) + " - " + str(payout.sales_period_end) + " - " + payout.market_place + " = " + str(len(temp))) royalty_items.append(temp)
渲染参数:
render(request, 'royalties/accounting_period/summary.html', {'detail_object': detail_object, 'payout_items': payout_items, 'royalty_items': royalty_items})
当前解决方案(自定义过滤器)
通过自定义过滤器实现索引访问:
@register.filter() def getRoyaltySet(royalty_items, outer_counter): return royalty_items[outer_counter]
更优雅的解决方案
方案1:视图层打包元组(推荐)
遵循Django“尽量将逻辑放在视图层”的原则,在视图中将payout对象和对应的royalties列表打包成元组,避免模板处理索引逻辑:
修改视图代码
detail_object = AccountingPeriod.objects.get(pk=detail_id) payout_items = Payout.objects.filter(sales_period_start__range=[detail_object.period_start, detail_object.period_end]) # 打包payout对象和对应的royalties列表 payout_royalty_pairs = [] for payout in payout_items: royalties = Royalty.objects.filter( entry_date__range=[payout.sales_period_start, payout.sales_period_end], market_place=payout.market_place ) payout_royalty_pairs.append( (payout, royalties) )
修改渲染参数
render(request, 'royalties/accounting_period/summary.html', { 'detail_object': detail_object, 'payout_royalty_pairs': payout_royalty_pairs })
优化后的模板代码
模板逻辑更简洁,直接遍历打包好的元组:
{% for payout, royalties in payout_royalty_pairs %} <h2>{{ payout.market_place }} on {{ payout.entry_date }}:</h2> <table> {% for royalty in royalties %} <tr> <th scope="row">{{ royalty.id }}</th> <td>{{ royalty.entry_date }}</td> <td>{{ royalty.market_place }}</td> </tr> {% endfor %} </table> {% endfor %}
方案2:模板层使用内置方法(无需修改视图)
如果不想调整视图,除了自定义过滤器,还可以利用Django模板的slice过滤器结合forloop.counter0,不过写法稍显繁琐:
{% for item in payout_items %} {% with outer_idx=forloop.counter0 %} <h2>{{ item.market_place }} on {{ item.entry_date }}:</h2> <table> {% for royalty in royalty_items|slice:outer_idx|slice:":1"|first %} <tr> <th scope="row">{{ royalty.id }}</th> <td>{{ royalty.entry_date }}</td> <td>{{ royalty.market_place }}</td> </tr> {% endfor %} </table> {% endwith %} {% endfor %}
但这种方式可读性不如方案1,因此更推荐视图层打包元组的方案。
内容的提问来源于stack exchange,提问作者Byte Insight
相关产品推荐
相关产品推荐

