1. render_template基础
render_template()是Flask中最常用的模板渲染函数。它的第一个参数是模板文件名,后面的关键字参数会作为变量传递给模板。
# 文件路径:app.py from flask import Flask, render_template app = Flask(__name__) @app.route("/") def index(): return render_template("index.html", title="RUNOOB 首页", name="World") @app.route("/user/<username>") def profile(username): return render_template("user.html", username=username, posts=[ {"title": "Flask 入门", "date": "2026-05-01"}, {"title": "Jinja2 模板", "date": "2026-05-10"}, ])模板文件放在项目目录下的templates/文件夹中:
runoob-flask-test/ ├── app.py └── templates/ ├── index.html └── user.html
templates/index.html:
<!DOCTYPE html> <html> <head> <title>{{ title }} - RUNOOB</title> </head> <body> <h1>{{ title }}</h1> <p>Hello {{ name }}。</p> </body> </html>2. Jinja2基础语法
Jinja2模板中三种核心语法:
| 语法 | 用途 | 示例 |
|---|---|---|
{{ ... }} | 输出变量值,自动转义HTML | {{ username }} |
{% ... %} | 控制语句(if、for、block等) | {% if user %} |
{# ... #} | 注释,不会出现在渲染结果中 | {# 这是注释 #} |
模板示例:条件判断与循环
templates/user.html:
<!DOCTYPE html> <html> <head> <title>{{ username }} - RUNOOB</title> </head> <body> <h1>{{ username }} 的个人主页</h1> <!-- 条件判断 --> {% if posts %} <h2>最近文章:</h2> <ul> <!-- 循环遍历 --> {% for post in posts %} <li>{{ loop.index }}. {{ post.title }} ({{ post.date }})</li> {% endfor %} </ul> {% else %} <p>暂无文章</p> {% endif %} <!-- 使用过滤器 --> <p>用户名大写:{{ username|upper }}</p> </body> </html>3. 常用过滤器
过滤器用于修改变量的输出格式,使用管道符|调用。
| 过滤器 | 作用 | 示例 | 输出 |
|---|---|---|---|
upper | 转大写 | {{ "hello"|upper }} | HELLO |
lower | 转小写 | {{ "HELLO"|lower }} | hello |
title | 首字母大写 | {{ "hello world"|title }} | Hello World |
length | 获取长度 | {{ [1,2,3]|length }} | 3 |
default | 设置默认值 | {{ name|default("匿名") }} | 匿名(当name为空时) |
safe | 标记为安全HTML(不转义) | {{ "<b>粗体</b>"|safe }} | 渲染为粗体文字 |
join | 连接列表 | {{ ["a","b"]|join(",") }} | a,b |
⚠️安全警告:
safe过滤器会关闭自动转义,只在绝对信任数据来源时使用。对于用户输入的内容,永远不要使用safe。
4. 自动转义——XSS防护
Jinja2默认会对所有变量输出进行HTML转义,这是Web安全的一道关键防线。
例如,如果用户提交的用户名是<script>alert("xss")</script>,模板渲染时会自动转换为安全文本:
<!-- 模板中直接使用变量 --> <p>{{ username }}</p> <!-- 渲染结果(HTML特殊字符已被转义) --> <p>&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;</p>转义适用于.html、.htm、.xml、.xhtml、.svg结尾的模板文件。
5. 模板继承——消除重复代码
模板继承是Jinja2最强大的功能之一,它让你可以定义一个「基础布局」,然后通过子模板填充内容。这避免了在每个页面中重复编写头部、导航、页脚等公共部分。
基础模板(父模板)
templates/base.html:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>{% block title %}RUNOOB Flask 教程{% endblock %}</title> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> </head> <body> <header> <h1>RUNOOB Flask 教程</h1> <nav> <a href="/">首页</a> | <a href="/about">关于</a> </nav> </header> <main> {% block content %}{% endblock %} </main> <footer> <p>© 2026 RUNOOB.com</p> </footer> </body> </html>子模板(继承父模板)
templates/index.html:
{% extends "base.html" %} {% block title %}RUNOOB 教程 - 首页{% endblock %} {% block content %} <h2>欢迎来到 RUNOOB</h2> <p>{{ greeting }}</p> {% if user %} <p>当前用户:{{ user }}</p> {% else %} <p><a href="/login">请先登录</a></p> {% endif %} {% endblock %}| 标签 | 作用 |
|---|---|
{% extends "base.html" %} | 声明当前模板继承自base.html(必须放在第一行) |
{% block name %}...{% endblock %} | 定义一个可被子模板覆盖的区域 |
{{ super() }} | 在子模板block中调用父模板同名block的内容 |
使用模板继承后,你只需要修改
base.html一处,所有页面的公共部分(如导航、页脚)都会自动更新。
6. 模板中的内置对象
以下Flask对象在模板中可以直接使用,无需通过render_template()传递:
| 对象 | 说明 | 模板中使用示例 |
|---|---|---|
request | 当前请求对象 | {{ request.path }} |
session | 当前会话数据 | {{ session.get("username") }} |
g | 请求级全局变量 | {{ g.user }} |
config | 应用配置 | {{ config["APP_NAME"] }} |
url_for() | URL生成函数 | {{ url_for("index") }} |
get_flashed_messages() | 获取flash消息 | {% for msg in get_flashed_messages() %} |
7. 包含其他模板——include
使用{% include %}可以在一个模板中嵌入另一个模板,适用于导航栏、侧边栏等可复用组件。
templates/_navbar.html(下划线开头表示这是一个局部模板):
<nav style="background:#f0f0f0;padding:10px;"> <a href="/">首页</a> | <a href="/posts">文章</a> | <a href="/about">关于</a> </nav>
在任意模板中引入:
<!DOCTYPE html> <html> <head><title>页面</title></head> <body> {% include "_navbar.html" %} <h1>页面内容</h1> <p>这是页面主体内容。</p> </body> </html>最佳实践:模板继承(
extends)用于页面级的布局复用,include用于组件级的代码复用。
8. 完整示例:博客首页
app.py:
from flask import Flask, render_template app = Flask(__name__) @app.route("/") def index(): articles = [ {"id": 1, "title": "Flask 入门指南", "author": "runoob", "views": 1024}, {"id": 2, "title": "Jinja2 模板详解", "author": "admin", "views": 512}, {"id": 3, "title": "RESTful API 设计", "author": "runoob", "views": 256}, ] return render_template("blog.html", articles=articles)templates/blog.html:
{% extends "base.html" %} {% block title %}RUNOOB 博客{% endblock %} {% block content %} <h2>最新文章</h2> {% if articles %} {% for article in articles %} <div class="article" {% if article.views > 1000 %}style="background:#fff3cd;"{% endif %}> <h3>{{ article.title }}</h3> <p>作者:{{ article.author }} | 浏览:{{ article.views }}</p> {% if not loop.last %} <hr> {% endif %} </div> {% endfor %} {% else %} <p>暂时没有文章,请稍后再来。</p> {% endif %} {% endblock %}9. Jinja2循环特殊变量
在{% for %}循环中,Jinja2提供了以下内置变量:
| 变量 | 说明 |
|---|---|
loop.index | 当前循环索引,从1开始 |
loop.index0 | 当前循环索引,从0开始 |
loop.first | 是否为第一次循环 |
loop.last | 是否为最后一次循环 |
loop.length | 序列总长度 |
10. 模板语法速查表
| 用途 | 语法 | 示例 |
|---|---|---|
| 变量输出 | {{ variable }} | {{ username }} |
| 变量输出(带默认值) | {{ variable|default("默认") }} | {{ name|default("匿名") }} |
| 条件判断 | {% if condition %}...{% endif %} | {% if user %} |
| 条件判断(多分支) | {% if %}...{% elif %}...{% else %}...{% endif %} | 同上 |
| 循环遍历 | {% for item in list %}...{% endfor %} | {% for post in posts %} |
| 循环(空时) | {% for %}...{% else %}...{% endfor %} | 列表为空时显示备用内容 |
| 模板继承 | {% extends "base.html" %} | 必须放在第一行 |
| 定义块 | {% block name %}...{% endblock %} | {% block content %} |
| 包含模板 | {% include "partial.html" %} | 组件复用 |
| 注释 | {# 注释内容 #} | 不会出现在渲染结果中 |
小结
本章全面讲解了Flask的模板渲染系统。render_template()是渲染模板的核心函数;Jinja2的{{ }}用于变量输出(自动转义防XSS),{% %}用于控制语句(if判断、for循环);过滤器通过|修改变量输出格式;模板继承通过{% extends %}和{% block %}消除重复代码,实现页面级布局复用;{% include %}实现组件级复用;模板中可直接使用request、session、url_for()等内置对象。模板引擎让业务逻辑与页面展示分离,是构建动态Web应用的关键工具。