bigywater.tech
Flask · 61

模板中的惰性对象

模板里随手写一个 a.books|length,背后可能是一次数据库查询。Query、生成器、缺失的变量在模板里分别会怎样?这篇用 SQL 计数和对照实验把它们一一暴露出来。

21 → 2
20 个作者的 N+1:懒加载 21 条 SELECT 对 selectinload 2 条
第二遍为空
同一个生成器在模板里循环第二次的结果
被吞掉
property 里抛出 AttributeError 时模板的表现

N+1

实测 · 本机执行输出
  循环里访问 a.books|length(懒加载)                    查询阶段  1 条 + 渲染阶段 20 条 = 共 21 条 SELECT
  语句示例: ['SELECT author.id, author.name ', 'SELECT book.id, book.title, book.author_id ', 'SELECT book.id, book.title, book.author_id ']
  视图里 selectinload 预加载                         查询阶段  2 条 + 渲染阶段  0 条 = 共  2 条 SELECT
  视图里 joinedload 预加载                           查询阶段  1 条 + 渲染阶段  0 条 = 共  1 条 SELECT

上一篇:Jinja2 宏;下一篇:模板渲染异常处理。N+1 的数据库专题见N+1 查询问题。

Query 对象

实测 · 本机执行输出
  模板里循环两遍 Query                                查询阶段  0 条 + 渲染阶段  2 条 = 共  2 条 SELECT
  先 .all() 再循环两遍                               查询阶段  1 条 + 渲染阶段  0 条 = 共  1 条 SELECT
  模板里调 q.count() 再循环一遍                         查询阶段  0 条 + 渲染阶段  2 条 = 共  2 条 SELECT

生成器

实测 · 本机执行输出
  循环两遍同一个生成器: '012|'
  先 |list: '012|012'

Undefined 的行为

实测 · 本机执行输出
  {{ missing }}                                    → 
  {{ missing.attr }}                               → UndefinedError: 'missing' is undefined
  {{ missing|default("默认") }}                      → 默认
  {{ d.k }}                                        → 
  {{ d["k"] }}                                     → 
  {{ d.k.z }}                                      → UndefinedError: 'dict object' has no attribute 'k'
  {% if missing %}真{% else %}假{% endif %}          → 假
  {% if d.k.z is defined %}已定义{% else %}未定义{% endif %} → UndefinedError: 'dict object' has no attribute 'k'
  {{ o.good }}|{{ o.nope }}|[{{ o.bad }}]          → ok||[]
  {{ o.method }}                                   → <bound method Obj.method of <__main__.Obj object at 0x109d2ce10>>
  {{ o.method() }}                                 → m
  {{ missing + 1 }}                                → UndefinedError: 'missing' is undefined
  {{ missing|length }}                             → 0
  {% for x in missing %}x{% endfor %}完             → 完

StrictUndefined

实测 · 本机执行输出
  Undefined        ['', '假', '默认']
  StrictUndefined  ['UndefinedError', 'UndefinedError', '默认']
  DebugUndefined   ['{{ missing }}', '假', '默认']

分步演示

惰性对象
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLAlchemy 2.1.1、Flask-SQLAlchemy(内存 SQLite);SQL 条数用 before_cursor_execute 事件统计
    • 没有验证:异步 SQLAlchemy 在模板里的表现、Pydantic 懒字段、lazy_gettext(Flask-Babel)这类惰性字符串对象、对象的 __getattr__ 动态属性被模板访问时的行为
    京ICP备2026067502号-1京公网安备11010502063487号