第61课:Flask 路由、视图与模板
学习目标
完成本课学习后,你将能够:
- 理解 Flask 中"路由"的概念,并使用
@app.route()装饰器定义网站的 URL 与处理函数之间的映射关系。 - 编写处理具体业务逻辑的"视图"函数,并让其返回 HTML 字符串或渲染后的模板。
- 使用 Jinja2 模板引擎创建动态 HTML 页面,掌握模板中的变量输出、控制结构(如循环、条件判断)等核心语法。
- 初步理解并实践模板继承机制,以实现网页结构的复用。
核心概念
1. 路由:网站的导航地图
想象一下,你的网站是一个大型商场,不同的楼层和店铺就是不同的网页。路由就是商场里的导航地图或指示牌,它告诉顾客(浏览器):想去"关于我们"页面,就走 /about 这条路;想去"产品展示",就走 /products。
在 Flask 中,我们用 @app.route() 这个"装饰器"来绘制这张地图。它把一个 URL(如 /、/hello)和一个 Python 函数(视图函数)绑定在一起。当用户访问这个 URL 时,Flask 就会自动执行对应的函数。
# 当用户访问网站根目录 "/" 时,执行 index 函数
@app.route('/')
def index():
return "欢迎来到我的首页!"
# 当用户访问 "/about" 时,执行 about 函数
@app.route('/about')
def about():
return "这是关于我们页面。"
2. 视图函数:处理请求的"服务员"
视图函数是路由规则中指定的 Python 函数。当 Flask 接收到一个匹配的 URL 请求时,就会调用这个函数。它的主要职责是:
- 处理业务逻辑(如查询数据库、计算数据)。
- 返回一个"响应"给浏览器。这个响应可以是简单的文本字符串、一段 HTML 代码,或者(最常用)渲染一个模板后生成的完整 HTML 页面。
@app.route('/greet/<name>')
def greet_user(name): # 视图函数,接收 URL 中的参数
# 业务逻辑:拼接问候语
message = f"你好,{name}!欢迎回来。"
return message # 返回一个简单的字符串响应
3. 模板:动态 HTML 的蓝图
直接用 Python 拼接大段 HTML 字符串既痛苦又难以维护。模板解决了这个问题。它是一个包含占位符的 HTML 文件。Flask 使用强大的 Jinja2 模板引擎,它允许你在 HTML 中嵌入特殊的语法,用于:
- 输出变量:使用
{{ variable_name }}将 Python 变量的值插入到 HTML 中。 - 使用控制结构:使用
{% %}来编写类似 Python 的代码,如{% if ... %}条件判断和{% for ... %}循环,让页面内容可以动态变化。
模板文件通常放在应用根目录下的 templates 文件夹中。
模板 (templates/index.html) 示例:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title> <!-- 输出变量 title -->
</head>
<body>
<h1>用户列表</h1>
{% if users %} <!-- 如果有用户数据 -->
<ul>
{% for user in users %} <!-- 遍历用户列表 -->
<li>{{ user.name }} - {{ user.role }}</li> <!-- 输出每个用户的属性 -->
{% endfor %}
</ul>
{% else %} <!-- 否则 -->
<p>暂无用户数据。</p>
{% endif %}
</body>
</html>
4. 渲染模板:连接视图与模板
在视图函数中,我们使用 render_template() 函数来"渲染"一个模板文件。你可以将 Python 变量作为参数传递给模板,这些变量就会替换掉模板中的对应占位符。
from flask import render_template
@app.route('/')
def index():
# 准备要传递给模板的数据
page_title = "我的首页"
user_list = [
{'name': '张三', 'role': '管理员'},
{'name': '李四', 'role': '编辑'}
]
# 渲染模板,并传递变量
return render_template('index.html',
title=page_title, # 模板中用 {{ title }} 使用
users=user_list) # 模板中用 {{ users }} 和 {% for %} 使用
代码示例
下面我们创建一个完整的、包含多个路由和模板的 Flask 应用。
第一步:创建项目结构
my_flask_app/
│
├── app.py # 主应用文件
└── templates/ # 存放所有模板的文件夹(Flask会自动在此查找)
├── base.html # 基础模板(用于继承)
├── index.html # 首页模板
├── about.html # 关于页面模板
└── user.html # 用户信息模板
第二步:编写主应用文件 app.py
from flask import Flask, render_template
app = Flask(__name__)
# 路由1:首页
@app.route('/')
def index():
# 传递不同的变量到模板
products = [
{'name': '智能手机', 'price': 3999},
{'name': '笔记本电脑', 'price': 7999},
{'name': '智能手表', 'price': 1599}
]
return render_template('index.html',
title='欢迎来到商店',
products=products)
# 路由2:关于页面,静态内容
@app.route('/about')
def about():
return render_template('about.html', title='关于我们')
# 路由3:动态路由,URL中包含变量
@app.route('/user/<username>')
def user_profile(username):
# 模拟根据用户名查询信息
user_info = {
'name': username,
'email': f'{username}@example.com',
'joined': '2024-01-01'
}
return render_template('user.html', user=user_info)
# 启动应用(仅在开发时使用)
if __name__ == '__main__':
app.run(debug=True)
第三步:创建模板文件
templates/base.html (基础模板,包含公共结构):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{ title }} - 我的商店</title>
</head>
<body>
<nav>
<a href="{{ url_for('index') }}">首页</a> |
<a href="{{ url_for('about') }}">关于我们</a> |
<a href="{{ url_for('user_profile', username='example') }}">示例用户</a>
</nav>
<hr>
{% block content %}{% endblock %} <!-- 定义一个名为 content 的区块,供子模板填充 -->
<footer>
<p>© 2024 我的 Flask 商店</p>
</footer>
</body>
</html>
templates/index.html (继承 base.html):
{% extends "base.html" %}
{% block content %}
<h1>{{ title }}</h1>
<h2>我们的产品:</h2>
<ul>
{% for product in products %}
<li>{{ product.name }} - ¥{{ product.price }}</li>
{% endfor %}
</ul>
{% endblock %}
templates/about.html:
{% extends "base.html" %}
{% block content %}
<h1>{{ title }}</h1>
<p>我们是一家专注于提供高质量科技产品的在线商店。</p>
<p>成立于 2024 年,致力于为您带来最好的购物体验。</p>
{% endblock %}
templates/user.html:
{% extends "base.html" %}
{% block content %}
<h1>用户档案</h1>
<p><strong>姓名:</strong>{{ user.name }}</p>
<p><strong>邮箱:</strong>{{ user.email }}</p>
<p><strong>加入日期:</strong>{{ user.joined }}</p>
{% endblock %}
第四步:运行应用
- 确保已安装 Flask:
pip install flask - 在终端进入
my_flask_app目录,运行:python app.py - 在浏览器访问
http://127.0.0.1:5000,然后尝试访问其他页面。
实践练习
练习一:基础路由
创建一个 Flask 应用,定义以下路由:
/:返回字符串<h1>欢迎访问我的网站!</h1>/contact:返回字符串<p>联系邮箱:[email protected]</p>
预期输出:
访问 http://127.0.0.1:5000/ 显示一级标题"欢迎访问我的网站!"。
访问 http://127.0.0.1:5000/contact 显示段落"联系邮箱:[email protected]"。
练习二:带变量的路由
创建一个路由 /square/<int:number>,访问时,页面显示该数字的平方。
提示: 使用 <int:number> 来捕获 URL 中的整数参数。
预期输出:
访问 http://127.0.0.1:5000/square/5,页面显示 25 是 5 的平方。。
练习三:模板继承
基于上面的代码示例,完成以下任务:
- 在
templates文件夹中新建一个team.html。 - 修改
base.html,在导航栏中添加一个指向/team页面的链接。 - 在
app.py中添加一个/team路由,该路由渲染team.html模板,并传递一个包含多个团队成员名字的列表。 - 在
team.html中使用模板继承,并使用{% for %}循环显示所有团队成员。
预期输出: 导航栏中出现"团队"链接。点击后,页面显示所有团队成员的名字列表。
常见错误
-
TemplateNotFound错误:这是最常见的错误之一。原因通常是:- 模板文件名拼写错误。
- 模板文件没有放在
templates文件夹内,或者templates文件夹不在应用的根目录下。 - 使用了错误的路径,如
render_template('/templates/index.html'),正确应为render_template('index.html')。
-
路由重叠或顺序问题:Flask 会匹配第一个符合规则的路由。例如,
/user/<username>和/user/admin两个路由,/user/admin必须放在前面,否则会被第一个规则捕获,username变量会得到字符串"admin"。 -
在模板中误用 Python 语法:Jinja2 的语法与纯 Python 有细微区别。例如,在模板中调用函数不能用
(),应写成{{ func }}而不是{{ func() }}(除非该函数返回一个可调用对象)。使用{{ my_list|length }}而不是{{ len(my_list) }}。 -
变量作用域混淆:只有从视图函数通过
render_template()传递的变量,才能在模板中使用。试图在模板中直接使用未传递的 Python 变量会导致UndefinedError。
小结
本课我们学习了 Flask 应用构建的三大基石:
- 路由 (
@app.route()):负责将 URL 映射到特定的处理函数(视图函数),是用户请求进入应用的入口。 - 视图函数:负责处理具体的业务逻辑,并决定返回什么内容给用户。它可以是简单的字符串,也可以是渲染后的模板。
- 模板 (
render_template+ Jinja2):负责生成动态的 HTML 页面。通过{{ }}输出变量,通过{% %}使用控制结构,通过模板继承 ({% extends %},{% block %}) 实现代码复用。
理解并熟练运用这三者,你就能构建出结构清晰、功能完整的 Web 应用了。在下一课中,我们将学习如何处理用户通过表单提交的数据,让你的应用能够与用户进行真正的交互。