Django模板动态索引访问数组变量渲染值为空问题
Django模板的点.语法解析规则不支持将变量作为列表的动态索引使用。
你写{{ tmplAnnVar.annotationsId.c }}时,模板引擎不会把c识别为你通过with定义的循环计数器变量(值为0/1/2这类整数),而是会直接尝试查找annotationsId列表中键名为字符串"c"的成员或属性——列表本身不存在这个键,自然解析为空值,最终渲染出来的data属性就没有内容。
你的三组对照测试完全可以印证这个规则:
- 直接渲染
{{c}}能正常输出索引数字,说明循环计数器变量本身定义、传值都正常 - 写死字面量索引
{{ tmplAnnVar.annotationsId.0 }}能正常输出值,说明点语法接固定数字索引的逻辑是正常工作的 - 用变量
c当索引时值为空,核心就是模板引擎原生不支持动态索引解析,你一开始拆分三个平行数组靠索引对位的写法,在Django模板里没法直接生效。
优先推荐第一种方案,逻辑更清晰,后续维护成本更低:
方案1:视图层重组数据结构,废弃三个平行数组的传参方式
你在视图层遍历数据库返回的annotations结果时,直接把每条标注的id、code、name封装成字典,组成一个统一的列表传给模板,循环时直接读取单条对象的属性即可,完全不需要动态索引。
视图层修改代码:# 替换原来初始化三个空数组、循环追加值的逻辑 annotation_list = [] for annot in annotations: annotation_list.append({ "ann_id": annot["annotation_web_server_id"], "ann_code": annot["annotation_code"], "ann_name": annot["annotation_name"] }) tmplAnnVar = {} tmplAnnVar["messageSaveAndAnalize"] = messageSaveAndAnalize tmplAnnVar["annotation_list"] = annotation_list # 删除原来给annotationsId、annotationsCode、annotationsName三个数组赋值的代码模板部分修改为直接循环统一列表,不需要再拿循环计数器做索引:
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-lg-start"> {% for annot in tmplAnnVar.annotation_list %} <li><a class="dropdown-item active" href="#" data-active="1" data-annotation-web-server-id="{{ annot.ann_id }}" data-annotation-code="{{ annot.ann_code }}" data-func="{{ annot.ann_code }}">{{ annot.ann_name }}</a> </li> {% endfor %} </ul>方案2:保留平行数组结构,通过自定义模板过滤器实现动态索引
如果不想修改视图层的传参结构,可以写一个简单的自定义模板过滤器,传入列表和索引变量,返回对应位置的值。
第一步:在对应app目录下创建templatetags文件夹,新建ann_tags.py文件:from django import template register = template.Library() @register.filter def index_val(lst, idx): try: return lst[idx] except (IndexError, TypeError): return ""第二步:模板顶部加载自定义标签
{% load ann_tags %}第三步:原来的动态索引位置改为过滤器调用写法:
<ul class="dropdown-menu dropdown-menu-end dropdown-menu-lg-start"> {% for annotId in tmplAnnVar.annotationsId %} {% with c=forloop.counter0 %} <li><a class="dropdown-item active" href="#" data-active="1" data-annotation-web-server-id="{{ tmplAnnVar.annotationsId|index_val:c }}" data-annotation-code="{{ tmplAnnVar.annotationsCode|index_val:c }}" data-func="{{ tmplAnnVar.annotationsCode|index_val:c }}">{{ tmplAnnVar.annotationsName|index_val:c }}</a> </li> {% endwith %} {% endfor %} </ul>
注意:第一种方案不需要额外编写模板标签,数据和渲染逻辑对应关系更直白,后续如果要给标注新增字段(比如描述、创建时间)只需要在字典里加键值对即可,维护成本远低于第二种平行数组方案,优先选择第一种。
内容的提问来源于stack exchange,提问作者zabitstack

