Flask 框架基础

教师演示 45 分钟 + 学生练习 45 分钟  ·  前置条件:Linux 服务器 + Python 3 + 安全组已放行 5000 端口

🎯 学习目标
  • 能够安装 Flask 并运行第一个 Web 应用
  • 能够使用路由将不同网址映射到不同的函数
  • 能够使用 Jinja2 模板渲染带样式的 HTML 页面
第一部分:教师演示(45 分钟)

一、Flask 是什么?

📖 定义
Flask 是一个用 Python 编写的轻量级 Web 框架,基于 Werkzeug 和 Jinja2 构建。
💬 人话翻译
你写 Python 代码,Flask 帮你把它变成浏览器能访问的网页。

Flask 核心只做两件事:

1
接收请求
用户在浏览器输入网址
2
执行函数
Flask 找到对应的 Python 函数并运行
3
返回响应
把结果显示在浏览器上

二、安装 Flask 并运行第一个程序

2.1 搭建环境

Bash
# 创建工作目录并进入
mkdir -p ~/flask_learn && cd ~/flask_learn

# 创建 Python 虚拟环境(把项目依赖和系统隔离,互不干扰)
python3 -m venv venv

# 激活虚拟环境(成功后命令行前面出现 (venv) 标志)
source venv/bin/activate

# 安装 Flask
pip install flask

2.2 创建第一个程序

Bash
nano app.py
Python · app.py
from flask import Flask          # 从 flask 包导入 Flask 类

app = Flask(__name__)            # 创建应用实例

# @app.route('/') 叫做"路由装饰器"
# 作用:当用户访问网站首页(/)时,执行下面的函数
@app.route('/')
def index():
    return '<h1>🎉 Hello Flask!</h1><p>我的第一个 Flask 应用成功运行!</p>'

# 启动应用
# host='0.0.0.0'  → 允许外网访问
# port=5000       → 监听 5000 端口
# debug=True      → 调试模式,改代码后自动重启
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

2.3 运行并查看效果

Bash
python app.py

浏览器访问 http://你的服务器IP:5000

▶ 浏览器显示效果
🎉 Hello Flask!
我的第一个 Flask 应用成功运行!
💡 核心概念:路由
@app.route('/') 把网址 / 和函数 index() 绑定在一起。
用户访问这个网址 → Flask 执行这个函数 → 函数的返回值就是页面内容。

三、路由——不同网址显示不同内容

Ctrl+C 停止程序,编辑 app.py,替换全部内容:

Python · app.py(替换全部)
from flask import Flask

app = Flask(__name__)

# ---- 路由1:首页 ----
@app.route('/')
def index():
    return '''
    <h1>🏠 Flask 路由演示</h1>
    <ul>
        <li><a href="/about">📖 关于本站</a></li>
        <li><a href="/user/张三">👤 用户:张三</a></li>
        <li><a href="/user/李四">👤 用户:李四</a></li>
        <li><a href="/score/95">📊 查分:95</a></li>
        <li><a href="/score/48">📊 查分:48</a></li>
        <li><a href="/calc/10/20">🧮 计算 10+20</a></li>
    </ul>
    '''

# ---- 路由2:固定路由 ----
@app.route('/about')
def about():
    return '<h1>📖 关于本站</h1><p>保山学院 Flask 学习项目。</p><a href="/">← 返回</a>'

# ---- 路由3:带变量的路由 ----
# <name> 是路由变量:访问 /user/张三 时,name 就等于 "张三"
@app.route('/user/<name>')
def user(name):
    return f'<h1>👤 你好,{name}!</h1><a href="/">← 返回</a>'

# ---- 路由4:限定变量类型为整数 ----
# <int:score> 表示 score 必须是整数
@app.route('/score/<int:score>')
def show_score(score):
    if score >= 90:
        level = '🌟 优秀'
    elif score >= 60:
        level = '✅ 及格'
    else:
        level = '❌ 不及格'
    return f'<h1>📊 {score} 分 → {level}</h1><a href="/">← 返回</a>'

# ---- 路由5:多个变量 ----
@app.route('/calc/<int:a>/<int:b>')
def calc(a, b):
    return f'<h1>🧮 {a} + {b} = {a + b}</h1><a href="/">← 返回</a>'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
Bash
python app.py

浏览器访问首页,点击每个链接看效果:

访问地址页面效果涉及知识点
/首页,显示 6 个链接固定路由
/about关于本站固定路由
/user/张三显示"你好,张三"动态路由变量 <name>
/score/9595 分 → 🌟 优秀类型限定 <int:score>
/score/4848 分 → ❌ 不及格if/elif/else 条件判断
/calc/10/2010 + 20 = 30多个变量
📋 路由知识小结
固定路由@app.route('/about') → 网址固定不变
动态路由@app.route('/user/<name>') → URL 中的值自动传给函数参数
类型限定<int:score> → 只接受整数,非整数返回 404

四、模板——把 HTML 和 Python 分开

💬 为什么需要模板?
前面我们把 HTML 直接写在 Python 代码里——页面一复杂就乱了。
模板的作用:把 HTML 单独放到文件里,Python 只负责传数据,Jinja2 引擎负责渲染。
类比:模板就是一张带空格的试卷,Python 负责填答案,Jinja2 负责印出来。

Ctrl+C 停止程序。

4.1 创建目录 + CSS 样式 + 基础模板

Bash
# 创建模板目录和静态文件目录
mkdir -p templates static
Bash
nano static/style.css
📄 static/style.css(点击展开完整代码)
CSS · static/style.css
/* 全局样式 */
body {
    font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
    max-width: 750px;
    margin: 0 auto;
    padding: 20px;
    background: #f0f4f8;
    color: #333;
    line-height: 1.8;
}
/* 导航栏 */
nav {
    background: #2c3e50;
    padding: 12px 20px;
    border-radius: 8px;
    margin-bottom: 25px;
}
nav a { color: #fff; text-decoration: none; margin-right: 20px; }
nav a:hover { color: #3498db; }
/* 标题 */
h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 8px; }
/* 卡片 */
.card {
    background: #fff;
    border-radius: 10px;
    padding: 20px 25px;
    margin: 15px 0;
    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
/* 表格 */
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px 14px; text-align: left; border-bottom: 1px solid #eee; }
th { background: #2c3e50; color: #fff; }
tr:hover { background: #f1f8ff; }
/* 页脚 */
footer { text-align: center; color: #aaa; margin-top: 30px; padding-top: 15px; border-top: 1px solid #ddd; }
Bash
nano templates/base.html
HTML · templates/base.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Flask学习{% endblock %}</title>
    <!-- url_for 自动生成静态文件的正确路径 -->
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <!-- 导航栏:所有页面共享 -->
    <nav>
        <a href="/">🏠 首页</a>
        <a href="/user/同学">👤 用户页</a>
        <a href="/students">📋 学生列表</a>
    </nav>
    <!-- 子模板在这里填内容 -->
    {% block content %}{% endblock %}
    <footer>保山学院 · Flask 框架学习</footer>
</body>
</html>
💡 模板继承
base.html 是"骨架"(导航栏 + 页脚),子模板用 {% extends "base.html" %} 继承它,只写不同的内容。好处:改一处,所有页面一起变。

4.2 创建首页模板 → 立即看效果

Bash
nano templates/index.html
HTML · templates/index.html
<!-- 继承基础模板,自动获得导航栏和页脚 -->
{% extends "base.html" %}
{% block title %}首页{% endblock %}

{% block content %}
<h1>🎉 Flask 模板演示</h1>
<div class="card">
    <h2>📚 什么是模板?</h2>
    <p>模板 = 带空格的 HTML。Python 传数据,Jinja2 填进去。</p>
    <p>当前时间:<strong>{{ now }}</strong></p>
</div>
<div class="card">
    <h2>🧪 Jinja2 核心语法</h2>
    <p><code>{{ "{{ 变量 }}" }}</code> → 输出变量的值</p>
    <p><code>{{ "{% if 条件 %}" }}</code> → 条件判断</p>
    <p><code>{{ "{% for x in 列表 %}" }}</code> → 循环</p>
    <p><code>{{ "{{ 变量 | 过滤器 }}" }}</code> → 处理后再输出</p>
</div>
{% endblock %}

更新 app.py 主程序:

Python · app.py(替换全部)
from flask import Flask, render_template
from datetime import datetime

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html',
        now=datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
Bash
python app.py
▶ 浏览器效果
带导航栏、卡片样式、当前时间的漂亮首页已经出来了!此时导航栏的"用户页"和"学生列表"还不能用,下面逐一添加。

4.3 添加用户页模板 → 刷新看效果

不需要停止程序(debug 模式下新增模板后刷新浏览器即可)。

Bash
nano templates/user.html
HTML · templates/user.html
{% extends "base.html" %}
{% block title %}用户:{{ name }}{% endblock %}

{% block content %}
<h1>👤 用户页面</h1>
<div class="card">
    <h2>模板变量 & 过滤器</h2>
    <p>原始值:<strong>{{ name }}</strong></p>
    <!-- | upper 过滤器:转为大写 -->
    <p>转大写:<strong>{{ name | upper }}</strong></p>
    <!-- | length 过滤器:计算长度 -->
    <p>字符数:<strong>{{ name | length }}</strong></p>
</div>
<div class="card">
    <h2>条件判断(if / elif / else)</h2>
    {% if name == "管理员" %}
        <p style="color:red;">⚠️ 欢迎管理员,您拥有最高权限!</p>
    {% elif name | length > 4 %}
        <p>你的名字挺长的(超过4个字符)!</p>
    {% else %}
        <p>{{ name }},欢迎你来学 Flask!</p>
    {% endif %}
</div>
{% endblock %}

在 app.py 的 if __name__ 之前追加一个路由:

Python · 追加到 app.py
@app.route('/user/<name>')
def user_page(name):
    return render_template('user.html', name=name)
▶ 浏览器效果
访问 /user/张三 → 看到变量和过滤器效果
访问 /user/管理员 → if 条件判断生效,红色管理员提示

4.4 添加学生列表模板 → 刷新看效果

Bash
nano templates/students.html
HTML · templates/students.html
{% extends "base.html" %}
{% block title %}学生列表{% endblock %}

{% block content %}
<h1>📋 学生信息列表</h1>
<div class="card">
    <p><strong>{{ students | length }}</strong> 名学生</p>
    <table>
        <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>专业</th>
            <th>成绩</th>
            <th>评定</th>
        </tr>
        <!-- for 循环遍历列表,loop.index 是从 1 开始的序号 -->
        {% for s in students %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ s.name }}</td>
            <td>{{ s.major }}</td>
            <td>{{ s.score }}</td>
            <td>
                {% if s.score >= 90 %}🌟 优秀
                {% elif s.score >= 60 %}✅ 及格
                {% else %}❌ 不及格
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </table>
</div>
{% endblock %}

在 app.py 的 if __name__ 之前再追加:

Python · 追加到 app.py
@app.route('/students')
def student_list():
    students = [
        {'name': '张三', 'major': '计算机科学', 'score': 92},
        {'name': '李四', 'major': '大数据技术', 'score': 78},
        {'name': '王五', 'major': '人工智能',   'score': 55},
        {'name': '赵六', 'major': '软件工程',   'score': 88},
        {'name': '孙七', 'major': '大数据技术', 'score': 95},
    ]
    return render_template('students.html', students=students)
▶ 浏览器效果
访问 /students → 带样式的表格,成绩旁自动显示🌟/✅/❌。
此时导航栏三个链接全部可用,可在各页面间自由切换。
📋 教师演示核心总结
路由@app.route() 把网址绑定到函数
模板render_template() 渲染 HTML,Python 只管传数据
Jinja2 语法{{ 变量 }}{% if %}{% for %}| 过滤器
第二部分:学生练习(45 分钟)

按顺序操作,每完成一步就刷新浏览器看效果

练习一:环境搭建 + 路由练习(15 分钟)

第 1 步:搭建环境

Bash
# 登录服务器
ssh root@你的IP

# 创建目录、虚拟环境、安装 Flask
mkdir -p ~/myflask && cd ~/myflask
python3 -m venv venv
source venv/bin/activate
pip install flask

第 2 步:创建 app.py 并运行

Bash
nano app.py
Python · app.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return '''
    <h1>🎉 你好,我是张三!</h1>
    <p>这是我的第一个 Flask 应用。</p>
    <hr>
    <h3>我的页面:</h3>
    <ul>
        <li><a href="/about">📖 关于我</a></li>
        <li><a href="/greet/老师">👋 问候老师</a></li>
        <li><a href="/greet/同桌">👋 问候同桌</a></li>
        <li><a href="/score/95">📊 查分 95</a></li>
        <li><a href="/score/42">📊 查分 42</a></li>
        <li><a href="/calc/12/30">🧮 计算 12+30</a></li>
        <li><a href="/weather/保山">🌤️ 保山天气</a></li>
        <li><a href="/weather/昆明">🌤️ 昆明天气</a></li>
    </ul>
    '''

@app.route('/about')
def about():
    return '<h1>📖 关于我</h1><p>我叫张三,大数据技术专业,喜欢编程和篮球。</p><a href="/">← 返回</a>'

@app.route('/greet/<name>')
def greet(name):
    return f'<h1>👋 {name},你好!</h1><p>很高兴认识你!</p><a href="/">← 返回</a>'

@app.route('/score/<int:score>')
def show_score(score):
    if score >= 90:
        result = '🌟 优秀'
    elif score >= 60:
        result = '✅ 及格'
    else:
        result = '❌ 不及格'
    return f'<h1>📊 {score} 分 → {result}</h1><a href="/">← 返回</a>'

@app.route('/calc/<int:a>/<int:b>')
def calc(a, b):
    return f'<h1>🧮 {a} + {b} = {a + b}</h1><a href="/">← 返回</a>'

@app.route('/weather/<city>')
def weather(city):
    if city == '保山':
        info = '☀️ 晴天 22℃'
    elif city == '昆明':
        info = '⛅ 多云 18℃'
    elif city == '北京':
        info = '🌬️ 大风 12℃'
    else:
        info = '🌍 暂无该城市天气数据'
    return f'<h1>🌤️ {city} 天气</h1><p>{info}</p><a href="/">← 返回</a>'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
⚠️ 注意
把代码中的"张三"和个人信息改成你自己的。
Bash
python app.py

浏览器访问 http://你的IP:5000,点击每个链接看效果。

练习二:模板渲染——给页面加上样式(30 分钟)

Ctrl+C 停止程序。

第 1 步:创建目录

Bash
cd ~/myflask
mkdir -p templates static

第 2 步:创建 CSS 样式文件

Bash
nano static/style.css
CSS · static/style.css
body {
    font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
    max-width: 700px; margin: 0 auto; padding: 20px;
    background: #f0f4f8; color: #333; line-height: 1.8;
}
nav { background: #2c3e50; padding: 12px 20px; border-radius: 8px; margin-bottom: 20px; }
nav a { color: #fff; text-decoration: none; margin-right: 18px; }
nav a:hover { color: #3498db; }
h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 8px; }
.card { background: #fff; border-radius: 10px; padding: 20px; margin: 12px 0;
        box-shadow: 0 2px 6px rgba(0,0,0,0.08); }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #eee; }
th { background: #2c3e50; color: #fff; }
tr:hover { background: #f1f8ff; }
footer { text-align: center; color: #aaa; margin-top: 30px; }

第 3 步:创建基础模板

Bash
nano templates/base.html
HTML · templates/base.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Flask练习{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <nav>
        <a href="/">🏠 首页</a>
        <a href="/about">📖 关于我</a>
        <a href="/classmates">📋 同学列表</a>
    </nav>
    {% block content %}{% endblock %}
    <footer>保山学院 · 我的 Flask 练习</footer>
</body>
</html>

第 4 步:创建首页模板 + 更新 app.py → 看效果

Bash
nano templates/index.html
HTML · templates/index.html
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block content %}
<h1>🎉 我的 Flask 练习</h1>
<div class="card">
    <p>👤 姓名:<strong>{{ my_name }}</strong></p>
    <p>🎓 专业:<strong>{{ my_major }}</strong></p>
    <p>🕐 当前时间:<strong>{{ now }}</strong></p>
</div>
<div class="card">
    <h2>🔗 快速导航</h2>
    <p><a href="/about">📖 关于我</a> · <a href="/classmates">📋 同学列表</a></p>
</div>
{% endblock %}
Bash
nano app.py
Python · app.py(替换全部)
from flask import Flask, render_template
from datetime import datetime

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html',
        my_name='张三',           # ← 改成你的名字
        my_major='大数据技术',     # ← 改成你的专业
        now=datetime.now().strftime('%Y-%m-%d %H:%M:%S'))

@app.route('/about')
def about():
    return render_template('about.html',
        my_name='张三',           # ← 改成你的名字
        hobbies=['编程', '篮球', '听音乐', '打游戏'])  # ← 改成你的爱好

@app.route('/greet/<name>')
def greet(name):
    return f'<h1>👋 {name},你好!</h1><a href="/">← 返回</a>'

@app.route('/classmates')
def classmates():
    data = [
        {'name': '张三', 'hobby': '编程',   'score': 92},
        {'name': '李四', 'hobby': '篮球',   'score': 78},
        {'name': '王五', 'hobby': '画画',   'score': 55},
        {'name': '赵六', 'hobby': '编程',   'score': 88},
        {'name': '孙七', 'hobby': '跑步',   'score': 95},
    ]
    return render_template('classmates.html', classmates=data)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
Bash
python app.py

浏览器访问 http://你的IP:5000,看到带导航栏和卡片样式的首页。此时"关于我"和"同学列表"还不能用——下面马上创建。

第 5 步:创建"关于我"模板 → 刷新看效果

Bash
nano templates/about.html
HTML · templates/about.html
{% extends "base.html" %}
{% block title %}关于我{% endblock %}
{% block content %}
<h1>📖 关于我</h1>
<div class="card">
    <p>大家好,我叫 <strong>{{ my_name }}</strong>。</p>
    <h3>我的爱好:</h3>
    <ul>
        {% for hobby in hobbies %}
            <li>{{ hobby }}</li>
        {% endfor %}
    </ul>
    <p>共 <strong>{{ hobbies | length }}</strong> 个爱好。</p>
</div>
{% endblock %}

浏览器访问 http://你的IP:5000/about,看到你的爱好列表。

第 6 步:创建"同学列表"模板 → 刷新看效果

Bash
nano templates/classmates.html
HTML · templates/classmates.html
{% extends "base.html" %}
{% block title %}同学列表{% endblock %}
{% block content %}
<h1>📋 我的同学们</h1>
<div class="card">
    <p>共 <strong>{{ classmates | length }}</strong> 位同学</p>
    <table>
        <tr>
            <th>序号</th><th>姓名</th><th>爱好</th>
            <th>成绩</th><th>评定</th>
        </tr>
        {% for c in classmates %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ c.name }}</td>
            <td>{{ c.hobby }}</td>
            <td>{{ c.score }}</td>
            <td>
                {% if c.score >= 90 %}🌟 优秀
                {% elif c.score >= 60 %}✅ 及格
                {% else %}❌ 不及格
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </table>
</div>
{% endblock %}

浏览器访问 http://你的IP:5000/classmates,看到完整的同学列表表格。导航栏三个链接全部可用。

📖 知识点速查

概念说明类比
@app.route('/')路由:网址 → 函数门牌号 → 房间
<name>动态路由变量快递单上的收件人
<int:score>限制变量为整数只收数字的输入框
render_template()渲染模板把答案填进试卷
{{ 变量 }}输出变量值填空题的横线
{% if %}条件判断如果…就…
{% for %}循环逐个点名
{% extends %}模板继承共用同一栋楼
{{ x | upper }}过滤器加工处理
url_for()自动生成 URL自动导航

⚠️ 常见问题

问题解决方法
浏览器访问不了检查安全组是否放行 5000 端口;确认代码里写了 host='0.0.0.0'
改了代码没生效确认 debug=True;或 Ctrl+C 后重新运行 python app.py
TemplateNotFound检查文件是否在 templates/ 文件夹里,文件名拼写是否一致
样式没生效CSS 是否在 static/ 文件夹里;浏览器 Ctrl+Shift+R 强制刷新