模板中{% for %}循环必须用引擎语法而非Python代码,Django用{% for item in data %}...{% empty %},Jinja2支持{% for k,v in data.items() %}...{% else %};空数据需传[]而非None;禁止在循环内调用有副作用的方法或触发N+1查询。

模板里用 {% for %} 循环输出数据,不是写 Python 代码
视图模板(比如 Django、Jinja2)里的循环不是 Python 语法,不能直接写 for item in data:,必须用模板引擎规定的标签语法。常见错误是把 Python 写法照搬进模板,结果报错 TemplateSyntaxError: Invalid block tag 或直接静默失败。
实操建议:
- Django 模板中必须用
{% for item in data %}...{% endfor %},data是视图传入的上下文变量名,不能是表达式(如data.items()) - Jinja2 类似,但支持更多操作,比如
{% for k, v in data.items() %}是合法的;Django 不支持这种写法,会报错Could not parse the remainder - 循环内访问属性用点号:
{{ item.name }},不是方括号:{{ item['name'] }}(Django 默认不支持,Jinja2 支持但不推荐混用)
空列表时显示提示文字,别靠 Python 层判断
很多人在视图里加 if not data: context['empty_msg'] = '暂无数据',再在模板里 {{ empty_msg }} —— 这多了一层逻辑,也容易漏传。模板本身就能处理空状态。
实操建议:
- Django 用
{% empty %}子句:{% for item in data %}...{% empty %}<p>暂无数据</p><div class="aritcle_card flexRow"> <div class="artcardd flexRow"> <a class="aritcle_card_img" href="/ai/1884" title="MidReal AI"><img src="https://img.php.cn/upload/ai_manual/000/000/000/175680419787349.jpg" alt="MidReal AI" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a href="/ai/1884" title="MidReal AI">MidReal AI</a> <p>MidReal AI是一款革命性的AI小说生成工具,同时也是一个文本互动冒险游戏平台。</p> </div> <a href="/ai/1884" title="MidReal AI" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>{% endfor %} - Jinja2 用
{% else %}:{% for item in data %}...{% else %}<p>暂无数据</p>{% endfor %} - 注意:如果
data是None而非空列表,两种引擎都会报错(Invalid argument或UndefinedError),必须确保视图传的是空列表[],不是None
{% for %} 里不能调用带副作用的函数
模板不是执行环境,设计上就禁止运行任意 Python 代码。如果在循环里写 {{ item.get_display_name() }},而这个方法内部有数据库查询或修改状态,轻则性能崩塌,重则引发并发问题。
实操建议:
- 所有耗时或有副作用的逻辑,必须在视图层预处理好,比如用
annotate()或列表推导提前算好字段,再传进模板 - 模板过滤器(如
|date、|default)是安全的,但自定义过滤器也要遵守“只读、无 IO、无状态变更”原则 - 如果真需要动态计算,优先考虑前端 JS 渲染,而不是硬塞进模板循环
嵌套循环性能差,尤其在数据库查询未优化时
写 {% for user in users %}{% for post in user.posts.all %}... 看似自然,实际会触发 N+1 查询——每个 user.posts.all 都是一次 DB 请求,100 个用户就是 100 次查询。
实操建议:
- Django 必须用
prefetch_related('posts')或select_related()提前关联加载 - Jinja2 没有 ORM 集成,得靠视图层把数据组织成嵌套结构再传入,比如
{'users': [{'name': 'A', 'posts': [...]}, ...]} - 模板里嵌套超过两层(如 for → for → for)基本说明数据结构没理清,该重构视图逻辑了
最常被忽略的一点:循环变量作用域只在 {% for %}...{% endfor %} 内部,循环结束后 item 就不可用了。有人想在循环外写 {{ item.name }} 试图取最后一个值,结果什么也不输出——这不是 bug,是设计如此。









