本节摘要:上一节用字符串拼接 HTML,代码丑、易错、没法维护。模板引擎 Jinja2 把网页搬进独立文件,Python 数据通过占位符注入——数据归 Python,页面归模板,各回各家。本节讲模板语法三件套(变量、循环、判断)、模板继承消除重复、静态文件存放样式脚本,最后把轻记账 v0.5 的界面彻底翻新。
阅读完本节,你应当能够:
回看 4.3 的 index 视图:列表项是一行行 f-string。三个毛病会随页面复杂度指数级放大——引号嵌套地狱,加个属性就不知道哪层引号被吃掉;转义漏洞,用户在用途里输入一段 HTML 代码就能破坏页面结构,这是真实的注入攻击;职责纠缠,改个按钮颜色也要动 Python 代码。模板引擎的解法:HTML 住回自己的文件,Python 数据通过占位符"寄"进去。

项目根目录下建 templates 文件夹(Flask 默认到此找模板),放入 index.html:
<h1>轻记账</h1> <p>本月合计 {{ total }} 元,共 {{ count }} 笔</p> {% for record in records %} <li>{{ record.item }} — {{ record.amount }} 元</li> {% endfor %} {% if total > 500 %} <p style="color: red">本月花得有点猛,注意节奏。</p> {% else %} <p>消费在预算内,继续保持。</p> {% endif %}
双花括号是变量坑位,渲染时填入真值;花括号加百分号是逻辑块,管循环与分支;自动转义默认开启——用户输入的任何 HTML 特殊字符都会被转成无害文本,字符串拼接时代的注入漏洞直接被框架没收。视图函数瘦身成三行:
from flask import render_template @app.route("/") def index(): records = ledger.all() return render_template( "index.html", records=records, total=sum(r["amount"] for r in records), count=len(records), )
render_template 找到模板、注入数据、返回成品 HTML。传参用关键字参数,模板里用同名变量——视图管造数据,模板管摆造型,边界从此清晰。
每个页面都要导航栏和页脚,复制粘贴到第三页你就该意识到错了。Jinja2 的继承机制:先写一个"骨架模板",留出可替换的坑位:
<!-- templates/base.html --> <html> <head> <title>{% block title %}轻记账{% endblock %}</title> <link rel="stylesheet" href="/static/style.css"> </head> <body> <nav>轻记账 · 首页 · 统计</nav> {% block content %}{% endblock %} <footer>轻记账 v0.5</footer> </body> </html>
子模板声明继承,只填自己的坑位:
{% extends "base.html" %} {% block content %} <h1>本月支出</h1> {% for record in records %} <li>{{ record.item }} — {{ record.amount }} 元</li> {% endfor %} {% endblock %}
改导航栏只需动 base.html,全站生效——这就是"消除重复"在页面层的含义,和函数抽公共逻辑是同一个思想。
Flask 约定项目根下的 static 文件夹存放静态资源,模板里用固定的地址前缀引用:
项目结构(目录树示意): 主脚本 —— 路由与视图 templates 文件夹 —— base.html、index.html static 文件夹 —— style.css、app.js
<link rel="stylesheet" href="/static/style.css"> <script src="/static/app.js"></script>
浏览器每访问一个页面,对每个静态文件都会再发一次独立请求(4.1 的知识:一次页面 = 一次 HTML 请求 + N 次资源请求)。所以静态文件要精简,第 6 章讲缓存与压缩时会回到这里优化加载速度。
把 4.2 写的静态原型拆成 base.html 与 index.html,样式搬进 static 里的样式文件,视图改用 render_template。验收清单:刷新首页看到账目列表与合计;提交表单后重定向回新界面;故意在用途里输入一段 HTML 标签文字,确认页面把它原样显示而非执行——自动转义在保护你。变式一:给统计页做一个 stats.html,继承 base,按月显示支出。变式二:在模板里用循环变量 loop.index 给列表加序号,查文档找它的用法——学会查模板文档比记住每个细节更重要。
v0.5 界面齐整了。但数据还睡在 JSON 文件里,多用户一上就会打架——第 5 章,给数据一个正经的家。