Django模板系统为了保证安全性和简洁性,设计了一套独立于Python的变量解析规则,其中就明确不支持使用方括号来访问列表、字典等容器的索引或键,而是统一使用点号作为访问分隔符。很多刚接触Django的开发者会沿用Python的编码习惯,在模板中写出类似list[0]的语法,最终只会得到渲染报错或者空值的结果。

Django模板的变量解析规则
Django模板在解析变量时,会按照固定的优先级依次尝试不同的访问方式,点号后面的内容会被依次按照以下规则解析:
- 首先尝试作为字典的键访问,比如
dict.key会去字典中找key对应的value - 如果上一步失败,尝试作为对象的属性访问,比如实例的实例变量、绑定方法
- 如果还是失败,尝试作为对象的索引访问,比如列表的第n个元素、元组的内容
- 如果所有方式都失败,变量会被渲染为空字符串
也就是说,当你在模板中写my_list.0时,Django模板引擎会自动识别这是要访问列表的第0个索引,而不是把它当成字典键或者对象属性,这就是点号可以访问列表索引的核心原因。
错误用法与正确用法对比
假设我们在视图函数中传递了一个列表数据到模板:
# views.py
from django.shortcuts import render
def index(request):
student_list = ["张三", "李四", "王五"]
return render(request, "index.html", {"students": student_list})
下面是模板中的错误写法和正确写法对比:
| 写法类型 | 模板代码 | 渲染结果 |
|---|---|---|
| 错误写法 | {{ students[0] }} | 空字符串,控制台可能抛出模板语法错误 |
| 正确写法 | {{ students.0 }} | 张三 |
| 正确写法 | {{ students.2 }} | 王五 |
常见场景的访问示例
访问嵌套结构的数据
如果列表中的元素是字典或者对象,同样可以用点号链式访问:
# views.py 传递嵌套数据
def index(request):
user_list = [
{"name": "张三", "age": 20},
{"name": "李四", "age": 22}
]
return render(request, "index.html", {"users": user_list})
模板中访问第一个用户的姓名:
<p>第一个用户姓名:{{ users.0.name }}</p>
渲染结果为:第一个用户姓名:张三
循环中结合forloop访问索引
如果需要在循环中访问当前元素的索引,不需要手动写索引值,Django模板内置的forloop变量已经提供了相关属性:
<ul>
{% for user in users %}
<li>第{{ forloop.counter }}个用户:{{ user.name }},年龄:{{ user.age }}</li>
{% endfor %}
</ul>
这里forloop.counter是从1开始的索引,forloop.counter0是从0开始的索引,不需要自己用点号拼接索引值,避免出错。
特殊场景的替代方案
如果遇到点号无法满足的复杂索引访问需求,比如需要访问倒数第n个元素、或者需要动态计算索引,可以通过自定义模板过滤器来实现:
# custom_filters.py
from django import template
register = template.Library()
@register.filter
def get_index(value, index):
"""获取列表指定索引的元素"""
try:
return value[int(index)]
except (IndexError, TypeError, ValueError):
return ""
在模板中加载过滤器后使用:
{% load custom_filters %}
<p>倒数第一个用户:{{ users|get_index:-1 }}</p>
这种方式可以把复杂的索引逻辑放到Python代码中实现,模板只负责简单的调用,既符合Django模板的设计理念,也能满足特殊场景的需求。
常见误区提醒
注意:点号后面的索引必须是整数格式的字符,比如students.0是有效的,students."0"或者students.zero都是无效的,会被解析为空值。
另外不要在模板中尝试用点号访问不存在的索引,比如列表只有3个元素,却写students.5,最终只会渲染为空字符串,不会抛出异常,调试的时候需要注意这一点。