ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

python中的flask框架Jinja 模板入门教程

python中的flask框架Jinja 模板入门教程 前言Flask 是 Python 生态里最常用的轻量级 Web 框架web framework之一它默认使用 Jinja 作为模板引擎template engine。当视图函数要返回一段 HTML 时把 HTML 硬编码在 Python 字符串里很快就没法维护模板template的作用就是把「页面长什么样」和「数据从哪来」分开Python 负责准备数据模板负责把数据填进 HTML 骨架里。初学者最常见的误解是以为模板里可以随便写 Python 代码。实际上 Jinja 是一门独立的模板语言{{ }}、{% %}这些语法由 Jinja 自己解析运行在受限的环境里不能import、不能调用任意函数、也没有常规的赋值语句只能在模板里用{% set %}声明局部变量。你写{% for x in items %}时执行的并不是 Python 的for而是 Jinja 自己实现的循环。第二个常见误解是把「Jinja」和「Jinja2」当成两个东西。Jinja2 是 2.x 时代的叫法现在这个库的包名仍然是jinja2但主版本已经到 3.x文档站上统一称 Jinja。本文统一写 Jinja示例同样适用于 Jinja 3.x。本文讲四件事Jinja 的基本语法、控制结构、模板继承以及 Flask 中如何渲染并传参。示例需要先安装依赖且正文里的代码都只能在装好 Flask 的环境里运行本文不代为执行。pip install flask一、Flask 与 Jinja 的关系Flask 自己不带模板语法它把渲染工作委托给 Jinja。你在视图函数里调用render_template()Flask 就去项目根目录下的templates文件夹里找同名文件把它交给 Jinja 渲染成最终字符串再作为 HTTP 响应返回。目录结构必须是这样Flask 靠这个约定找模板myapp/├── app.py└── templates/└── index.htmltemplates这个名字是 Flask 的默认值不能随便改。一个最小的应用长这样# 适用于 Python 3.8需先 pip install flaskfrom flask import Flask, render_templateapp Flask(__name__)app.route(/)def index():return render_template(index.html, title首页, userAlice)if __name__ __main__:app.run(debugTrue)这里render_template的第一个参数是模板文件名相对templates目录后面全部用关键字参数把数据传给模板。模板里能直接用的名字就是这些关键字参数的名字。二、三种定界符Jinja 的语法由三种成对出现的定界符组成记牢它们就掌握了八成定界符名称作用{{ ... }}表达式expression把求值结果输出到页面{% ... %}语句statement控制流程如 for / if / block{# ... #}注释comment不输出类似 HTML 注释但不会发到浏览器一个同时用到三者的模板{# templates/index.html —— 这是注释不会出现在最终 HTML 里 #}!doctype htmltitle{{ title }}/title{% if user %}h1Hello, {{ user }}!/h1{% else %}h1Hello, stranger!/h1{% endif %}注意几个细节。语句块{% %}里的变量不加花括号写{% if user %}而不是{% if {{ user }} %}。过滤器filter用竖线连接可以链式书写例如{{ name|upper|trim }}表示先转大写再去掉首尾空白。过滤器带参数时用函数调用的括号形式比如{{ items|join(, ) }}。三、取值foo.bar与foo[bar]的区别Jinja 里取属性有两种写法但它们的查找顺序相反这是很多人栽跟头的地方。{{ foo.bar }}先在foo上找名为bar的属性找不到再当成字典项foo[bar]都找不到就返回一个未定义undefined对象。{{ foo[bar] }}先当成字典项foo[bar]找不到再找属性bar。所以当对象同时有同名属性和字典键时两种写法会得到不同结果。用attr()过滤器可以强制只查属性用|map(attribute...)提取属性同理。变量没有传进来时默认行为是打印或迭代它会得到空字符串其它运算会报错。也就是说{{ missing }}渲染出来是空白不报错容易掩盖「参数名拼错」这种问题。四、控制结构for 与 if循环和条件几乎和 Python 一样但必须写{% endfor %}/{% endif %}收尾ul{% for item in items %}li{{ loop.index }}. {{ item }}/li{% endfor %}/ulloop是 Jinja 在循环里自动提供的特殊变量常用字段有loop.index从 1 开始、loop.index0从 0 开始、loop.first、loop.last。循环体里还能拿到loop.previtem、loop.nextitem用is defined判断是否存在。if支持elif、else也支持 Jinja 的测试test语法比如{% if user is defined %}、{% if items is not empty %}。需要说明的是Jinja 的循环不能break也不能在循环中途修改被迭代的对象。循环里用{% set %}赋的值在迭代结束后会被清掉活不到循环外。五、模板继承extends 与 block真实项目不会在每个 HTML 里重复写导航和页脚。Jinja 用「父模板 子模板」解决{# templates/base.html #}!doctype htmlhtmlheadtitle{% block title %}我的站点{% endblock %}/title/headbody{% block content %}{% endblock %}/body/html{# templates/home.html #}{% extends base.html %}{% block title %}首页 - {{ super() }}{% endblock %}{% block content %}p欢迎{{ user }}/p{% endblock %}要点{% extends %}必须是子模板里的第一个标签同名block由子模板覆盖父模板在块内调用super()可以保留父模板的原始内容。{% include %}则是把另一个模板就地插入用于「拆分片段」而不是「继承框架」。常见坑点分不清 Jinja 的自动转义(escape)与不转义❌ 直接把用户输入拼进 HTMLp{{ comment }}/p在autoescapeFalse的环境里会被注入脚本✅ Flask 默认对.html模板开启自动转义用户内容会变成lt;scriptgt;确知内容安全时才用{{ html|safe }}Jinja 裸环境jinja2.Environment()默认不开启自动转义需要显式设置autoescapeTrue忘了写结束标签❌{% for x in xs %}li{{ x }}/li会让 Jinja 在渲染时报TemplateSyntaxError: Unexpected end of template✅ 每个{% for %}配{% endfor %}每个{% if %}配{% endif %}在语句块里多写了花括号❌{% if {{ user }} %}Hello{% endif %}是语法错误✅ 语句块里直接写变量名{% if user %}Hello{% endif %}变量名拼错却毫无反应❌ 视图传的是user模板写{{ username }}页面只显示空白排查半天✅ 需要严格报错时在 Flask 里用app.jinja_env.undefined jinja2.StrictUndefined让未定义变量直接抛异常把业务逻辑塞进模板❌{% for row in rows %}{% if row.status a and row.total 100 %}...{% endif %}{% endfor %}越写越长无法测试✅ 在视图里先过滤、排序、算好模板只做展示{% for row in active_rows %}错用点号/下标取值❌ 对象同时有属性items和字典键items时想取键却写了{{ obj.items }}拿到的是方法对象✅ 明确要取键时用{{ obj[items] }}明确要取属性时用{{ obj|attr(items) }}用 include 代替 extends❌ 每个页面{% include header.html %}改布局要动所有文件✅ 用{% extends base.html %}继承骨架只覆盖需要变的block总结需求写法输出变量{{ name }}控制流程{% for %}/{% if %}必须配对结束注释{# ... #}模板继承{% extends %}{% block %}插入片段{% include %}Flask 传参render_template(x.html, keyvalue)Jinja 的核心就是「三种定界符 控制结构 继承」这几块先用小模板把{{ }}、{% for %}、{% extends %}用顺再逐步学过滤器和测试。记住它是一门受限的模板语言不是 Python把复杂逻辑留在视图层模板只负责展示代码就会清爽得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进