61·模块十三:Web开发基础进阶

Flask 路由、视图与模板

flaskroutestemplatesjinja2

第61课:Flask 路由、视图与模板

学习目标

完成本课学习后,你将能够:

  1. 理解 Flask 中"路由"的概念,并使用 @app.route() 装饰器定义网站的 URL 与处理函数之间的映射关系。
  2. 编写处理具体业务逻辑的"视图"函数,并让其返回 HTML 字符串或渲染后的模板。
  3. 使用 Jinja2 模板引擎创建动态 HTML 页面,掌握模板中的变量输出、控制结构(如循环、条件判断)等核心语法。
  4. 初步理解并实践模板继承机制,以实现网页结构的复用。

核心概念

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 %}

第四步:运行应用

  1. 确保已安装 Flask:pip install flask
  2. 在终端进入 my_flask_app 目录,运行:python app.py
  3. 在浏览器访问 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 的平方。

练习三:模板继承

基于上面的代码示例,完成以下任务:

  1. templates 文件夹中新建一个 team.html
  2. 修改 base.html,在导航栏中添加一个指向 /team 页面的链接。
  3. app.py 中添加一个 /team 路由,该路由渲染 team.html 模板,并传递一个包含多个团队成员名字的列表。
  4. team.html 中使用模板继承,并使用 {% for %} 循环显示所有团队成员。

预期输出: 导航栏中出现"团队"链接。点击后,页面显示所有团队成员的名字列表。

常见错误

  1. TemplateNotFound 错误:这是最常见的错误之一。原因通常是:

    • 模板文件名拼写错误。
    • 模板文件没有放在 templates 文件夹内,或者 templates 文件夹不在应用的根目录下。
    • 使用了错误的路径,如 render_template('/templates/index.html'),正确应为 render_template('index.html')
  2. 路由重叠或顺序问题:Flask 会匹配第一个符合规则的路由。例如,/user/<username>/user/admin 两个路由,/user/admin 必须放在前面,否则会被第一个规则捕获,username 变量会得到字符串 "admin"

  3. 在模板中误用 Python 语法:Jinja2 的语法与纯 Python 有细微区别。例如,在模板中调用函数不能用 (),应写成 {{ func }} 而不是 {{ func() }}(除非该函数返回一个可调用对象)。使用 {{ my_list|length }} 而不是 {{ len(my_list) }}

  4. 变量作用域混淆:只有从视图函数通过 render_template() 传递的变量,才能在模板中使用。试图在模板中直接使用未传递的 Python 变量会导致 UndefinedError

小结

本课我们学习了 Flask 应用构建的三大基石:

  • 路由 (@app.route()):负责将 URL 映射到特定的处理函数(视图函数),是用户请求进入应用的入口。
  • 视图函数:负责处理具体的业务逻辑,并决定返回什么内容给用户。它可以是简单的字符串,也可以是渲染后的模板。
  • 模板 (render_template + Jinja2):负责生成动态的 HTML 页面。通过 {{ }} 输出变量,通过 {% %} 使用控制结构,通过模板继承 ({% extends %}, {% block %}) 实现代码复用。

理解并熟练运用这三者,你就能构建出结构清晰、功能完整的 Web 应用了。在下一课中,我们将学习如何处理用户通过表单提交的数据,让你的应用能够与用户进行真正的交互。

练习编辑器

python
Loading...

继续学习

完成本课后,建议继续学习下一课「Flask 表单处理与用户输入」 以巩固所学知识。