Python 从入门到实战(十二):Flask Web 开发(把学生成绩系统变成在线应用)

举报
倔强的石头_ 发表于 2026/01/05 10:58:41 2026/01/05
【摘要】 本文介绍了如何使用Flask框架将本地学生成绩管理系统升级为Web应用。主要内容包括: Flask基础:从安装到实现"Hello World"Web服务 核心概念:路由、视图函数和模板的使用 项目实战: 搭建项目结构 使用模板继承创建HTML页面 整合Pandas数据处理 最终实现功能: 学生列表展示 成绩查询 可视化报告生成 通过Flask框架,可以将之前的本地Python程

c6e3154d215982a75f2280a6ca6650b4.jpeg

文章目录


欢迎回到「Python 从入门到实战」系列专栏。上一篇咱们用 Matplotlib 和 Seaborn 实现了学生成绩的可视化,生成了美观的图表报告,但这些功能都局限在本地 —— 只有在自己的电脑上才能运行,老师和学生无法通过浏览器远程访问。


今天咱们要学 Python 的轻量级 Web 框架 ——Flask。它就像一座 “桥梁”,能把咱们之前写的本地学生成绩管理、数据可视化逻辑,快速升级为在线 Web 应用,让多人通过浏览器访问、查询成绩、查看可视化报告。咱们会从 Flask 基础入手,逐步实现 “首页展示→学生列表→成绩查询→可视化报告” 的完整功能,最终打造一个能实际使用的 “在线学生成绩管理系统”。

一、为什么选择 Flask?Web 开发的 “入门钥匙”

在学具体操作前,先搞懂 “为什么要做 Web 应用” 和 “为什么选 Flask”:

  • 本地项目的局限:之前的学生系统只能在自己电脑上运行,无法共享;数据和图表都存在本地文件,多人协作不方便;
  • Web 应用的优势:只要有浏览器(电脑、手机都可以),就能访问系统;数据集中存储,多人实时查看最新成绩;
  • Flask 的特点:轻量级、易上手,不需要复杂配置,适合快速把本地项目升级为 Web 应用;支持整合 Pandas、Matplotlib 等库,完美衔接咱们之前的代码。

简单说:Flask 能以最低的学习成本,让你的本地工具变成 “人人可用的在线服务”。

二、Flask 基础:从 “Hello World” 到 Web 服务

首先,咱们先搞定 Flask 的安装和核心概念,用 3 行代码跑通第一个 Web 服务,建立基础认知。

1. 安装 Flask 与依赖

Flask 是第三方库,需要先安装。另外,咱们还要用到 Pandas(处理数据)、Matplotlib(生成图表),所以一起安装:

bash

# 安装Flask核心库
pip install flask
# 安装依赖(数据处理和可视化)
pip install pandas matplotlib seaborn

安装完成后,验证是否成功:打开 Python 终端,输入import flask,没有报错就说明安装成功。

2. 核心概念:Flask 的 “三大件”

Flask 有三个核心概念,搞懂它们就能入门 Web 开发:

  1. 路由(Route):URL 地址与 Python 函数的映射关系。比如访问http://localhost:5000/(首页),对应执行index()函数;
  2. 视图函数(View Function):处理路由请求的 Python 函数,负责逻辑计算(比如读取数据、生成图表),并返回响应(比如 HTML 页面、字符串);
  3. 模板(Template):用于渲染 Web 页面的 HTML 文件,通过 Jinja2 模板引擎,能把 Python 变量(比如学生数据)动态插入到 HTML 中,让页面 “活” 起来。

3. 实战 1:跑通第一个 Flask 应用(Hello World)

创建一个名为app.py的文件(Flask 项目的主程序文件),写入以下代码:

python

# app.py(Flask主程序)
from flask import Flask

# 1. 初始化Flask应用(__name__表示当前文件作为应用入口)
app = Flask(__name__)

# 2. 定义路由:访问根URL(http://localhost:5000/)时,执行index()函数
@app.route('/')
def index():
    # 3. 视图函数:返回响应内容(这里是简单的字符串)
    return "<h1>欢迎访问学生成绩管理系统!</h1><p>这是用Flask开发的Web应用</p>"

# 4. 启动Web服务(只有直接运行app.py时才执行)
if __name__ == '__main__':
    # debug=True:开启调试模式,修改代码后自动重启服务,方便开发
    app.run(debug=True)

运行步骤:

  1. 打开终端,进入app.py所在的文件夹;

  2. 执行命令python app.py,看到以下输出:

    plaintext

    * Serving Flask app 'app'
    * Debug mode: on
    * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    
  3. 打开浏览器,访问http://localhost:5000/,就能看到页面显示:

    • 大标题 “欢迎访问学生成绩管理系统!”
    • 副标题 “这是用 Flask 开发的 Web 应用”

关键说明:

  • 调试模式debug=True非常重要,开发时开启,修改代码后不用手动重启服务;
  • URL 与路由@app.route('/')对应根 URL,如果你定义@app.route('/student'),访问http://localhost:5000/student才会触发对应的视图函数;
  • 响应内容:目前返回的是简单 HTML 字符串,后面会用模板返回更复杂的页面。

三、Flask 进阶:整合学生数据与 Web 页面

单纯的字符串响应不够美观,也无法展示复杂的学生数据。接下来,咱们用模板渲染 HTML 页面,把之前的学生 CSV 数据展示成 Web 表格,实现 “本地数据→Web 表格” 的跨越。

1. 项目结构:规范文件组织

随着功能增加,文件需要按规则存放,否则会混乱。创建以下项目结构(跟着手动建文件夹和文件):

plaintext

student_web/          # 项目根文件夹
├── app.py            # 主程序文件(Flask核心逻辑)
├── students_data.csv # 学生数据文件(之前生成的)
├── templates/        # 存放HTML模板的文件夹
│   ├── base.html     # 基础模板(所有页面继承这个)
│   ├── index.html    # 首页
│   └── student_list.html # 学生列表页
└── static/           # 存放静态文件(图片、CSS、JS)
    └── images/       # 存放可视化图表的文件夹

2. 模板继承:减少重复 HTML 代码

Web 开发中,多个页面(如首页、学生列表页)会有相同的头部(导航栏)和底部(版权信息),用模板继承可以避免重复写这些代码。

步骤 1:创建基础模板templates/base.html

html

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}学生成绩管理系统{% endblock %}</title>
    <!-- 引入简单的CSS,让页面更美观(Bootstrap,不用自己写CSS) -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <!-- 导航栏(所有页面都有) -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container">
            <a class="navbar-brand" href="/">学生成绩系统</a>
            <div class="collapse navbar-collapse">
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="nav-link" href="/">首页</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/student/list">学生列表</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/report">可视化报告</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 内容区域(子页面在这里填充不同内容) -->
    <div class="container mt-4">
        {% block content %}{% endblock %}
    </div>

    <!-- 底部信息(所有页面都有) -->
    <footer class="mt-5 py-3 bg-light text-center">
        <div class="container">
            <p class="mb-0">© 2024 学生成绩管理系统 | 用Flask开发</p>
        </div>
    </footer>

    <!-- Bootstrap的JS(可选,用于交互) -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

模板继承说明:

  • {% block title %}:子页面可以替换这里的标题;
  • {% block content %}:子页面的核心内容放在这里;
  • 引入 Bootstrap:这是一个免费的 CSS/JS 库,不用自己写样式就能让页面美观,新手友好。

3. 首页开发:templates/index.html

首页继承base.html,展示系统简介和快速入口:

html

<!-- templates/index.html -->
{% extends "base.html" %}

<!-- 替换标题 -->
{% block title %}首页 - 学生成绩管理系统{% endblock %}

<!-- 填充内容区域 -->
{% block content %}
<div class="jumbotron bg-light p-5 rounded-3">
    <h1 class="display-5 fw-bold">欢迎使用学生成绩管理系统</h1>
    <p class="lead mt-3">本系统基于Python Flask开发,整合Pandas数据处理和Matplotlib可视化,支持以下功能:</p>
    <ul class="list-group list-group-flush mt-3 w-50">
        <li class="list-group-item">查看所有学生的成绩列表</li>
        <li class="list-group-item">查询单个学生的详细成绩</li>
        <li class="list-group-item">查看各科成绩的可视化报告</li>
    </ul>
    <div class="mt-4">
        <a href="/student/list" class="btn btn-primary me-3">进入学生列表</a>
        <a href="/report" class="btn btn-success">查看可视化报告</a>
    </div>
</div>
{% endblock %}

4. 学生列表页:展示 Pandas 处理的数据

接下来,在app.py中添加路由和视图函数,用 Pandas 读取students_data.csv,传递到模板,渲染成 Web 表格。

步骤 1:修改app.py,添加学生列表路由

python

# app.py(新增代码,放在index路由后面)
import pandas as pd
from flask import Flask, render_template  # 新增render_template,用于渲染模板

app = Flask(__name__)

# 首页路由(之前的代码)
@app.route('/')
def index():
    # 渲染index.html模板,不再返回字符串
    return render_template('index.html')

# 新增:学生列表路由
@app.route('/student/list')
def student_list():
    # 1. 用Pandas读取学生数据(和之前本地处理一样)
    df = pd.read_csv('students_data.csv', encoding='utf-8', dtype={'course_score': int})
    # 添加成绩等级列(复用之前的逻辑)
    df['grade'] = df['course_score'].apply(
        lambda x: 'A级(90+)' if x >= 90 else ('B级(80-89)' if x >= 80 else 'C级(<80)')
    )
    # 2. 将DataFrame转换为字典列表,方便模板渲染(Flask不支持直接传递DataFrame)
    student_data = df.to_dict('records')  # 每一行是一个字典,键是列名
    
    # 3. 计算统计信息(传递到模板展示)
    total_students = df['name'].nunique()  # 总学生数
    total_courses = df['course_name'].nunique()  # 总课程数
    
    # 4. 渲染student_list.html,传递数据到模板
    return render_template(
        'student_list.html',
        students=student_data,  # 学生数据列表
        total_students=total_students,  # 总学生数
        total_courses=total_courses  # 总课程数
    )

# 启动服务(之前的代码)
if __name__ == '__main__':
    app.run(debug=True)

步骤 2:创建学生列表模板templates/student_list.html

html

<!-- templates/student_list.html -->
{% extends "base.html" %}

{% block title %}学生列表 - 学生成绩管理系统{% endblock %}

{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
    <h2>学生成绩列表</h2>
    <p class="text-muted">总学生数:{{ total_students }} 人 | 总课程数:{{ total_courses }} 门</p>
</div>

<!-- 学生成绩表格 -->
<table class="table table-striped table-hover">
    <thead class="table-dark">
        <tr>
            <th>学生姓名</th>
            <th>年龄</th>
            <th>课程名称</th>
            <th>成绩(分)</th>
            <th>成绩等级</th>
        </tr>
    </thead>
    <tbody>
        <!-- 循环渲染学生数据 -->
        {% for student in students %}
        <tr>
            <td>{{ student.name }}</td>
            <td>{{ student.age }}</td>
            <td>{{ student.course_name }}</td>
            <td>{{ student.course_score }}</td>
            <td>
                <!-- 根据等级显示不同颜色 -->
                {% if student.grade == 'A级(90+)' %}
                <span class="badge bg-success">{{ student.grade }}</span>
                {% elif student.grade == 'B级(80-89)' %}
                <span class="badge bg-warning">{{ student.grade }}</span>
                {% else %}
                <span class="badge bg-danger">{{ student.grade }}</span>
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>
{% endblock %}

运行测试:

  1. 确保students_data.csv在项目根目录;
  2. 运行app.py,访问http://localhost:5000/student/list,就能看到:
    • 顶部显示总学生数和课程数;
    • 美观的表格,展示每个学生的姓名、年龄、课程、成绩;
    • 成绩等级用不同颜色的标签显示(A 级绿色、B 级黄色、C 级红色)。

四、Flask + 可视化:Web 页面展示图表

上一篇咱们生成了柱状图、饼图,但只能存在本地文件。现在要让这些图表在 Web 页面展示,步骤是:

  1. 在 Flask 中生成图表,保存到static/images/文件夹;
  2. 在模板中引用静态文件夹的图片路径,渲染到页面。

1. 修改app.py,添加可视化报告路由

python

# app.py(新增代码,放在student_list路由后面)
import matplotlib.pyplot as plt
import seaborn as sns
import os

# 配置中文字体(避免图表中文乱码)
plt.rcParams['font.sans-serif'] = ['SimHei', 'PingFang SC']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style("whitegrid")

# 确保static/images文件夹存在(不存在则创建)
if not os.path.exists('static/images'):
    os.makedirs('static/images')

# 新增:可视化报告路由
@app.route('/report')
def report():
    # 1. 读取数据(复用之前的逻辑)
    df = pd.read_csv('students_data.csv', encoding='utf-8', dtype={'course_score': int})
    df['grade'] = df['course_score'].apply(
        lambda x: 'A级(90+)' if x >= 90 else ('B级(80-89)' if x >= 80 else 'C级(<80)')
    )
    
    # 2. 生成各科平均分柱状图
    course_avg = df.groupby('course_name')['course_score'].mean().round(1)
    fig, ax = plt.subplots(figsize=(6, 4), dpi=100)
    bars = ax.bar(course_avg.index, course_avg.values, color='skyblue', edgecolor='black')
    ax.set_title('各科平均分对比', fontsize=12)
    ax.set_xlabel('课程名称')
    ax.set_ylabel('平均分(分)')
    ax.set_ylim(80, 90)
    # 添加数值标签
    for bar in bars:
        height = bar.get_height()
        ax.text(bar.get_x()+bar.get_width()/2, height+0.5, str(height), ha='center', va='bottom')
    # 保存图表到static/images
    plt.savefig('static/images/course_avg.png', bbox_inches='tight')
    plt.close()  # 关闭图表,释放内存
    
    # 3. 生成成绩等级分布饼图
    grade_count = df['grade'].value_counts()
    fig, ax = plt.subplots(figsize=(6, 6), dpi=100)
    colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
    wedges, texts, autotexts = ax.pie(
        grade_count.values, labels=grade_count.index, autopct='%1.1f%%',
        startangle=90, colors=colors, explode=(0.05, 0, 0)
    )
    ax.set_title('成绩等级分布', fontsize=12)
    for autotext in autotexts:
        autotext.set_color('white')
    plt.savefig('static/images/grade_pie.png', bbox_inches='tight')
    plt.close()
    
    # 4. 渲染报告模板,传递图表路径
    return render_template('report.html')

2. 创建可视化报告模板templates/report.html

html

<!-- templates/report.html -->
{% extends "base.html" %}

{% block title %}可视化报告 - 学生成绩管理系统{% endblock %}

{% block content %}
<h2>学生成绩可视化报告</h2>
<p class="text-muted mb-4">基于学生成绩数据生成的图表分析</p>

<!-- 图表展示(2行1列,响应式布局) -->
<div class="row g-4">
    <!-- 柱状图:各科平均分 -->
    <div class="col-md-6">
        <div class="card shadow-sm">
            <div class="card-body">
                <h5 class="card-title">各科平均分对比</h5>
                <!-- 引用静态文件夹的图片:url_for('static', filename='图片路径') -->
                <img src="{{ url_for('static', filename='images/course_avg.png') }}" 
                     alt="各科平均分柱状图" class="img-fluid">
            </div>
        </div>
    </div>
    
    <!-- 饼图:成绩等级分布 -->
    <div class="col-md-6">
        <div class="card shadow-sm">
            <div class="card-body">
                <h5 class="card-title">成绩等级分布</h5>
                <img src="{{ url_for('static', filename='images/grade_pie.png') }}" 
                     alt="成绩等级饼图" class="img-fluid">
            </div>
        </div>
    </div>
</div>
{% endblock %}

关键说明:

  • url_for('static', filename='images/course_avg.png'):Flask 的辅助函数,自动生成静态文件的 URL,避免手动写路径出错;
  • img-fluid:Bootstrap 的类,让图片自适应页面宽度,在手机上也能正常显示;
  • card shadow-sm:给图表加卡片样式和阴影,让页面更有层次感。

运行测试:

访问http://localhost:5000/report,就能看到两个图表在 Web 页面上展示,和之前本地生成的一样,但现在任何人都能通过浏览器访问。

五、交互功能:学生成绩查询

只展示还不够,还要支持 “用户输入姓名,查询该学生的成绩”。这需要用到 Flask 的表单处理,接收用户输入的姓名,查询数据后返回结果。

1. 修改app.py,添加查询路由

python

# app.py(新增代码,放在report路由后面)
from flask import request  # 新增request对象,用于接收请求数据

# 新增:学生查询路由(支持GET和POST请求)
@app.route('/student/search', methods=['GET', 'POST'])
def student_search():
    # 如果是GET请求(用户刚访问页面),返回空结果的表单
    if request.method == 'GET':
        return render_template('student_search.html')
    
    # 如果是POST请求(用户提交表单),处理查询
    elif request.method == 'POST':
        # 1. 获取用户输入的姓名(从表单中取name为'student_name'的值)
        student_name = request.form.get('student_name', '').strip()
        
        # 2. 验证输入(不能为空)
        if not student_name:
            return render_template('student_search.html', error='请输入学生姓名!')
        
        # 3. 读取数据并查询
        df = pd.read_csv('students_data.csv', encoding='utf-8', dtype={'course_score': int})
        df['grade'] = df['course_score'].apply(
            lambda x: 'A级(90+)' if x >= 90 else ('B级(80-89)' if x >= 80 else 'C级(<80)')
        )
        # 筛选该学生的数据
        student_df = df[df['name'] == student_name]
        
        # 4. 处理查询结果
        if student_df.empty:
            # 没有找到该学生
            return render_template(
                'student_search.html',
                error=f'未找到名为“{student_name}”的学生',
                input_name=student_name  # 回显用户输入的姓名
            )
        else:
            # 找到学生,转换为字典列表
            student_data = student_df.to_dict('records')
            # 计算该学生的总分和平均分
            total_score = student_df['course_score'].sum()
            avg_score = student_df['course_score'].mean().round(1)
            return render_template(
                'student_search.html',
                student=student_data,  # 学生成绩数据
                total_score=total_score,  # 总分
                avg_score=avg_score,  # 平均分
                input_name=student_name  # 回显用户输入的姓名
            )

2. 创建查询模板templates/student_search.html

html

<!-- templates/student_search.html -->
{% extends "base.html" %}

{% block title %}学生查询 - 学生成绩管理系统{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-8">
        <h2>学生成绩查询</h2>
        
        <!-- 成绩查询表单 -->
        <form method="POST" class="mt-4">
            <div class="input-group mb-3">
                <input type="text" name="student_name" class="form-control" 
                       placeholder="请输入学生姓名(如:小明)" 
                       value="{{ input_name if input_name else '' }}">  <!-- 回显输入 -->
                <button class="btn btn-primary" type="submit">查询</button>
            </div>
            
            <!-- 显示错误信息(如果有) -->
            {% if error %}
            <div class="alert alert-danger" role="alert">
                {{ error }}
            </div>
            {% endif %}
        </form>
        
        <!-- 显示查询结果(如果有) -->
        {% if student %}
        <div class="card mt-4">
            <div class="card-header bg-primary text-white">
                <h5 class="mb-0">
                    {{ input_name }} 的成绩详情
                    <span class="float-end">总分:{{ total_score }} | 平均分:{{ avg_score }}</span>
                </h5>
            </div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <thead>
                        <tr>
                            <th>课程名称</th>
                            <th>成绩(分)</th>
                            <th>成绩等级</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for item in student %}
                        <tr>
                            <td>{{ item.course_name }}</td>
                            <td>{{ item.course_score }}</td>
                            <td>
                                {% if item.grade == 'A级(90+)' %}
                                <span class="badge bg-success">{{ item.grade }}</span>
                                {% elif item.grade == 'B级(80-89)' %}
                                <span class="badge bg-warning">{{ item.grade }}</span>
                                {% else %}
                                <span class="badge bg-danger">{{ item.grade }}</span>
                                {% endif %}
                            </td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
        {% endif %}
    </div>
</div>
{% endblock %}

关键功能说明:

  • 请求方法methods=['GET', 'POST']表示该路由支持两种请求:GET(访问页面)、POST(提交表单);
  • 表单回显value="{{ input_name if input_name else '' }}"让用户查询失败时,输入框保留之前的内容,不用重新输入;
  • 错误提示:用 Bootstrap 的alert-danger类显示错误信息(如 “未找到学生”),用户体验更好。

运行测试:

  1. 访问http://localhost:5000/student/search
  2. 输入 “小明”,点击查询,会显示小明的课程、成绩、总分和平均分;
  3. 输入 “不存在的名字”,会显示错误提示 “未找到该学生”。

六、完整项目实战:在线学生成绩管理系统

现在,咱们的系统已经有了 4 个核心功能:首页、学生列表、可视化报告、成绩查询。最后,把导航栏和所有功能串联起来,确保用户能通过导航在各个页面间切换(之前的base.html已经做好了导航栏)。

最终项目结构回顾

plaintext

student_web/
├── app.py                  # 主程序(路由、数据处理、图表生成)
├── students_data.csv       # 学生数据(数据源)
├── templates/
│   ├── base.html           # 基础模板(导航栏、底部)
│   ├── index.html          # 首页(系统简介)
│   ├── student_list.html   # 学生列表(所有成绩表格)
│   ├── student_search.html # 成绩查询(交互功能)
│   └── report.html         # 可视化报告(图表展示)
└── static/
    └── images/
        ├── course_avg.png  # 柱状图(自动生成)
        └── grade_pie.png   # 饼图(自动生成)

运行完整系统的步骤

  1. 确保所有文件按上述结构存放;
  2. 安装所有依赖(flask、pandas、matplotlib、seaborn);
  3. 运行python app.py,打开浏览器访问http://localhost:5000/
  4. 通过导航栏切换 “首页→学生列表→成绩查询→可视化报告”,测试所有功能。

七、新手必踩的 5 个坑:避坑指南

Flask 开发虽然简单,但新手容易在 “路径”“请求处理”“静态文件” 上踩坑,总结如下:

坑 1:模板路径错误(找不到 HTML 文件)

python

# 错误示例:模板不在templates文件夹,或文件名写错
return render_template('studentlist.html')  # 错误:正确文件名是student_list.html

解决

  • 模板必须放在templates文件夹(名字不能错,全小写);
  • 渲染时文件名必须和实际一致,包括下划线、大小写(Flask 区分大小写)。

坑 2:静态文件引用错误(图表不显示)

html

<!-- 错误示例:直接写本地路径,Flask无法识别 -->
<img src="static/images/course_avg.png">

解决:用url_for生成静态文件 URL:

html

<img src="{{ url_for('static', filename='images/course_avg.png') }}">

坑 3:请求方法错误(表单提交没反应)

python

# 错误示例:路由只支持GET,无法处理POST请求
@app.route('/student/search')  # 默认methods=['GET']
def student_search():
    if request.method == 'POST':  # 永远不会执行
        pass

解决:路由必须显式指定支持 POST:

python

@app.route('/student/search', methods=['GET', 'POST'])

坑 4:数据读取路径错误(找不到 CSV 文件)

python

# 错误示例:CSV文件不在项目根目录,Pandas找不到
df = pd.read_csv('data/students_data.csv')  # 错误:路径不对

解决

  • 确保students_data.csvapp.py所在的根目录;

  • 或用绝对路径(不推荐,换电脑会失效):

    python

    import os
    csv_path = os.path.join(os.path.dirname(__file__), 'students_data.csv')
    df = pd.read_csv(csv_path)
    

坑 5:调试模式关闭(修改代码不生效)

python

# 错误示例:debug=False,修改代码后需要手动重启服务
app.run(debug=False)

解决:开发时务必开启调试模式:

python

app.run(debug=True)

注意:生产环境(给别人正式使用时)要关闭debug=True,否则有安全风险。

八、小结与下一篇预告

这篇你学到了什么?

  1. Flask 基础:理解路由、视图函数、模板的核心概念,跑通 Web 服务;
  2. 模板开发:用 Jinja2 模板继承减少重复代码,引入 Bootstrap 美化页面;
  3. 数据整合:把 Pandas 本地数据处理逻辑迁移到 Flask,渲染成 Web 表格;
  4. 可视化 Web 化:在 Flask 中生成图表,保存到静态文件夹,在页面展示;
  5. 交互功能:用 request 处理表单提交,实现学生成绩查询,提升用户体验;
  6. 项目实战:构建完整的在线学生成绩管理系统,掌握 Web 项目的文件结构和开发流程。

下一篇预告

今天的 Flask 应用已经能通过浏览器访问,但数据还是存在本地 CSV 文件中,多人同时修改会冲突,而且无法持久化存储新数据(比如新增学生)。下一篇咱们会学 Python 的数据库操作,用SQLite(轻量级数据库,无需安装)替换 CSV 文件,实现数据的增删改查,让 Web 应用真正支持多人协作,成为生产级别的系统。

如果这篇内容帮你搭建了第一个 Flask 应用,欢迎在评论区分享你的系统截图或遇到的问题,咱们一起交流进步~

c6e3154d215982a75f2280a6ca6650b4.jpeg

文章目录


欢迎回到「Python 从入门到实战」系列专栏。上一篇咱们用 Matplotlib 和 Seaborn 实现了学生成绩的可视化,生成了美观的图表报告,但这些功能都局限在本地 —— 只有在自己的电脑上才能运行,老师和学生无法通过浏览器远程访问。


今天咱们要学 Python 的轻量级 Web 框架 ——Flask。它就像一座 “桥梁”,能把咱们之前写的本地学生成绩管理、数据可视化逻辑,快速升级为在线 Web 应用,让多人通过浏览器访问、查询成绩、查看可视化报告。咱们会从 Flask 基础入手,逐步实现 “首页展示→学生列表→成绩查询→可视化报告” 的完整功能,最终打造一个能实际使用的 “在线学生成绩管理系统”。

一、为什么选择 Flask?Web 开发的 “入门钥匙”

在学具体操作前,先搞懂 “为什么要做 Web 应用” 和 “为什么选 Flask”:

  • 本地项目的局限:之前的学生系统只能在自己电脑上运行,无法共享;数据和图表都存在本地文件,多人协作不方便;
  • Web 应用的优势:只要有浏览器(电脑、手机都可以),就能访问系统;数据集中存储,多人实时查看最新成绩;
  • Flask 的特点:轻量级、易上手,不需要复杂配置,适合快速把本地项目升级为 Web 应用;支持整合 Pandas、Matplotlib 等库,完美衔接咱们之前的代码。

简单说:Flask 能以最低的学习成本,让你的本地工具变成 “人人可用的在线服务”。

二、Flask 基础:从 “Hello World” 到 Web 服务

首先,咱们先搞定 Flask 的安装和核心概念,用 3 行代码跑通第一个 Web 服务,建立基础认知。

1. 安装 Flask 与依赖

Flask 是第三方库,需要先安装。另外,咱们还要用到 Pandas(处理数据)、Matplotlib(生成图表),所以一起安装:

bash

# 安装Flask核心库
pip install flask
# 安装依赖(数据处理和可视化)
pip install pandas matplotlib seaborn

安装完成后,验证是否成功:打开 Python 终端,输入import flask,没有报错就说明安装成功。

2. 核心概念:Flask 的 “三大件”

Flask 有三个核心概念,搞懂它们就能入门 Web 开发:

  1. 路由(Route):URL 地址与 Python 函数的映射关系。比如访问http://localhost:5000/(首页),对应执行index()函数;
  2. 视图函数(View Function):处理路由请求的 Python 函数,负责逻辑计算(比如读取数据、生成图表),并返回响应(比如 HTML 页面、字符串);
  3. 模板(Template):用于渲染 Web 页面的 HTML 文件,通过 Jinja2 模板引擎,能把 Python 变量(比如学生数据)动态插入到 HTML 中,让页面 “活” 起来。

3. 实战 1:跑通第一个 Flask 应用(Hello World)

创建一个名为app.py的文件(Flask 项目的主程序文件),写入以下代码:

python

# app.py(Flask主程序)
from flask import Flask

# 1. 初始化Flask应用(__name__表示当前文件作为应用入口)
app = Flask(__name__)

# 2. 定义路由:访问根URL(http://localhost:5000/)时,执行index()函数
@app.route('/')
def index():
    # 3. 视图函数:返回响应内容(这里是简单的字符串)
    return "<h1>欢迎访问学生成绩管理系统!</h1><p>这是用Flask开发的Web应用</p>"

# 4. 启动Web服务(只有直接运行app.py时才执行)
if __name__ == '__main__':
    # debug=True:开启调试模式,修改代码后自动重启服务,方便开发
    app.run(debug=True)

运行步骤:

  1. 打开终端,进入app.py所在的文件夹;

  2. 执行命令python app.py,看到以下输出:

    plaintext

    * Serving Flask app 'app'
    * Debug mode: on
    * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    
  3. 打开浏览器,访问http://localhost:5000/,就能看到页面显示:

    • 大标题 “欢迎访问学生成绩管理系统!”
    • 副标题 “这是用 Flask 开发的 Web 应用”

关键说明:

  • 调试模式debug=True非常重要,开发时开启,修改代码后不用手动重启服务;
  • URL 与路由@app.route('/')对应根 URL,如果你定义@app.route('/student'),访问http://localhost:5000/student才会触发对应的视图函数;
  • 响应内容:目前返回的是简单 HTML 字符串,后面会用模板返回更复杂的页面。

三、Flask 进阶:整合学生数据与 Web 页面

单纯的字符串响应不够美观,也无法展示复杂的学生数据。接下来,咱们用模板渲染 HTML 页面,把之前的学生 CSV 数据展示成 Web 表格,实现 “本地数据→Web 表格” 的跨越。

1. 项目结构:规范文件组织

随着功能增加,文件需要按规则存放,否则会混乱。创建以下项目结构(跟着手动建文件夹和文件):

plaintext

student_web/          # 项目根文件夹
├── app.py            # 主程序文件(Flask核心逻辑)
├── students_data.csv # 学生数据文件(之前生成的)
├── templates/        # 存放HTML模板的文件夹
│   ├── base.html     # 基础模板(所有页面继承这个)
│   ├── index.html    # 首页
│   └── student_list.html # 学生列表页
└── static/           # 存放静态文件(图片、CSS、JS)
    └── images/       # 存放可视化图表的文件夹

2. 模板继承:减少重复 HTML 代码

Web 开发中,多个页面(如首页、学生列表页)会有相同的头部(导航栏)和底部(版权信息),用模板继承可以避免重复写这些代码。

步骤 1:创建基础模板templates/base.html

html

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}学生成绩管理系统{% endblock %}</title>
    <!-- 引入简单的CSS,让页面更美观(Bootstrap,不用自己写CSS) -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <!-- 导航栏(所有页面都有) -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <div class="container">
            <a class="navbar-brand" href="/">学生成绩系统</a>
            <div class="collapse navbar-collapse">
                <ul class="navbar-nav">
                    <li class="nav-item">
                        <a class="nav-link" href="/">首页</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/student/list">学生列表</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/report">可视化报告</a>
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- 内容区域(子页面在这里填充不同内容) -->
    <div class="container mt-4">
        {% block content %}{% endblock %}
    </div>

    <!-- 底部信息(所有页面都有) -->
    <footer class="mt-5 py-3 bg-light text-center">
        <div class="container">
            <p class="mb-0">© 2024 学生成绩管理系统 | 用Flask开发</p>
        </div>
    </footer>

    <!-- Bootstrap的JS(可选,用于交互) -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

模板继承说明:

  • {% block title %}:子页面可以替换这里的标题;
  • {% block content %}:子页面的核心内容放在这里;
  • 引入 Bootstrap:这是一个免费的 CSS/JS 库,不用自己写样式就能让页面美观,新手友好。

3. 首页开发:templates/index.html

首页继承base.html,展示系统简介和快速入口:

html

<!-- templates/index.html -->
{% extends "base.html" %}

<!-- 替换标题 -->
{% block title %}首页 - 学生成绩管理系统{% endblock %}

<!-- 填充内容区域 -->
{% block content %}
<div class="jumbotron bg-light p-5 rounded-3">
    <h1 class="display-5 fw-bold">欢迎使用学生成绩管理系统</h1>
    <p class="lead mt-3">本系统基于Python Flask开发,整合Pandas数据处理和Matplotlib可视化,支持以下功能:</p>
    <ul class="list-group list-group-flush mt-3 w-50">
        <li class="list-group-item">查看所有学生的成绩列表</li>
        <li class="list-group-item">查询单个学生的详细成绩</li>
        <li class="list-group-item">查看各科成绩的可视化报告</li>
    </ul>
    <div class="mt-4">
        <a href="/student/list" class="btn btn-primary me-3">进入学生列表</a>
        <a href="/report" class="btn btn-success">查看可视化报告</a>
    </div>
</div>
{% endblock %}

4. 学生列表页:展示 Pandas 处理的数据

接下来,在app.py中添加路由和视图函数,用 Pandas 读取students_data.csv,传递到模板,渲染成 Web 表格。

步骤 1:修改app.py,添加学生列表路由

python

# app.py(新增代码,放在index路由后面)
import pandas as pd
from flask import Flask, render_template  # 新增render_template,用于渲染模板

app = Flask(__name__)

# 首页路由(之前的代码)
@app.route('/')
def index():
    # 渲染index.html模板,不再返回字符串
    return render_template('index.html')

# 新增:学生列表路由
@app.route('/student/list')
def student_list():
    # 1. 用Pandas读取学生数据(和之前本地处理一样)
    df = pd.read_csv('students_data.csv', encoding='utf-8', dtype={'course_score': int})
    # 添加成绩等级列(复用之前的逻辑)
    df['grade'] = df['course_score'].apply(
        lambda x: 'A级(90+)' if x >= 90 else ('B级(80-89)' if x >= 80 else 'C级(<80)')
    )
    # 2. 将DataFrame转换为字典列表,方便模板渲染(Flask不支持直接传递DataFrame)
    student_data = df.to_dict('records')  # 每一行是一个字典,键是列名
    
    # 3. 计算统计信息(传递到模板展示)
    total_students = df['name'].nunique()  # 总学生数
    total_courses = df['course_name'].nunique()  # 总课程数
    
    # 4. 渲染student_list.html,传递数据到模板
    return render_template(
        'student_list.html',
        students=student_data,  # 学生数据列表
        total_students=total_students,  # 总学生数
        total_courses=total_courses  # 总课程数
    )

# 启动服务(之前的代码)
if __name__ == '__main__':
    app.run(debug=True)

步骤 2:创建学生列表模板templates/student_list.html

html

<!-- templates/student_list.html -->
{% extends "base.html" %}

{% block title %}学生列表 - 学生成绩管理系统{% endblock %}

{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
    <h2>学生成绩列表</h2>
    <p class="text-muted">总学生数:{{ total_students }} 人 | 总课程数:{{ total_courses }} 门</p>
</div>

<!-- 学生成绩表格 -->
<table class="table table-striped table-hover">
    <thead class="table-dark">
        <tr>
            <th>学生姓名</th>
            <th>年龄</th>
            <th>课程名称</th>
            <th>成绩(分)</th>
            <th>成绩等级</th>
        </tr>
    </thead>
    <tbody>
        <!-- 循环渲染学生数据 -->
        {% for student in students %}
        <tr>
            <td>{{ student.name }}</td>
            <td>{{ student.age }}</td>
            <td>{{ student.course_name }}</td>
            <td>{{ student.course_score }}</td>
            <td>
                <!-- 根据等级显示不同颜色 -->
                {% if student.grade == 'A级(90+)' %}
                <span class="badge bg-success">{{ student.grade }}</span>
                {% elif student.grade == 'B级(80-89)' %}
                <span class="badge bg-warning">{{ student.grade }}</span>
                {% else %}
                <span class="badge bg-danger">{{ student.grade }}</span>
                {% endif %}
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>
{% endblock %}

运行测试:

  1. 确保students_data.csv在项目根目录;
  2. 运行app.py,访问http://localhost:5000/student/list,就能看到:
    • 顶部显示总学生数和课程数;
    • 美观的表格,展示每个学生的姓名、年龄、课程、成绩;
    • 成绩等级用不同颜色的标签显示(A 级绿色、B 级黄色、C 级红色)。

四、Flask + 可视化:Web 页面展示图表

上一篇咱们生成了柱状图、饼图,但只能存在本地文件。现在要让这些图表在 Web 页面展示,步骤是:

  1. 在 Flask 中生成图表,保存到static/images/文件夹;
  2. 在模板中引用静态文件夹的图片路径,渲染到页面。

1. 修改app.py,添加可视化报告路由

python

# app.py(新增代码,放在student_list路由后面)
import matplotlib.pyplot as plt
import seaborn as sns
import os

# 配置中文字体(避免图表中文乱码)
plt.rcParams['font.sans-serif'] = ['SimHei', 'PingFang SC']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style("whitegrid")

# 确保static/images文件夹存在(不存在则创建)
if not os.path.exists('static/images'):
    os.makedirs('static/images')

# 新增:可视化报告路由
@app.route('/report')
def report():
    # 1. 读取数据(复用之前的逻辑)
    df = pd.read_csv('students_data.csv', encoding='utf-8', dtype={'course_score': int})
    df['grade'] = df['course_score'].apply(
        lambda x: 'A级(90+)' if x >= 90 else ('B级(80-89)' if x >= 80 else 'C级(<80)')
    )
    
    # 2. 生成各科平均分柱状图
    course_avg = df.groupby('course_name')['course_score'].mean().round(1)
    fig, ax = plt.subplots(figsize=(6, 4), dpi=100)
    bars = ax.bar(course_avg.index, course_avg.values, color='skyblue', edgecolor='black')
    ax.set_title('各科平均分对比', fontsize=12)
    ax.set_xlabel('课程名称')
    ax.set_ylabel('平均分(分)')
    ax.set_ylim(80, 90)
    # 添加数值标签
    for bar in bars:
        height = bar.get_height()
        ax.text(bar.get_x()+bar.get_width()/2, height+0.5, str(height), ha='center', va='bottom')
    # 保存图表到static/images
    plt.savefig('static/images/course_avg.png', bbox_inches='tight')
    plt.close()  # 关闭图表,释放内存
    
    # 3. 生成成绩等级分布饼图
    grade_count = df['grade'].value_counts()
    fig, ax = plt.subplots(figsize=(6, 6), dpi=100)
    colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']
    wedges, texts, autotexts = ax.pie(
        grade_count.values, labels=grade_count.index, autopct='%1.1f%%',
        startangle=90, colors=colors, explode=(0.05, 0, 0)
    )
    ax.set_title('成绩等级分布', fontsize=12)
    for autotext in autotexts:
        autotext.set_color('white')
    plt.savefig('static/images/grade_pie.png', bbox_inches='tight')
    plt.close()
    
    # 4. 渲染报告模板,传递图表路径
    return render_template('report.html')

2. 创建可视化报告模板templates/report.html

html

<!-- templates/report.html -->
{% extends "base.html" %}

{% block title %}可视化报告 - 学生成绩管理系统{% endblock %}

{% block content %}
<h2>学生成绩可视化报告</h2>
<p class="text-muted mb-4">基于学生成绩数据生成的图表分析</p>

<!-- 图表展示(2行1列,响应式布局) -->
<div class="row g-4">
    <!-- 柱状图:各科平均分 -->
    <div class="col-md-6">
        <div class="card shadow-sm">
            <div class="card-body">
                <h5 class="card-title">各科平均分对比</h5>
                <!-- 引用静态文件夹的图片:url_for('static', filename='图片路径') -->
                <img src="{{ url_for('static', filename='images/course_avg.png') }}" 
                     alt="各科平均分柱状图" class="img-fluid">
            </div>
        </div>
    </div>
    
    <!-- 饼图:成绩等级分布 -->
    <div class="col-md-6">
        <div class="card shadow-sm">
            <div class="card-body">
                <h5 class="card-title">成绩等级分布</h5>
                <img src="{{ url_for('static', filename='images/grade_pie.png') }}" 
                     alt="成绩等级饼图" class="img-fluid">
            </div>
        </div>
    </div>
</div>
{% endblock %}

关键说明:

  • url_for('static', filename='images/course_avg.png'):Flask 的辅助函数,自动生成静态文件的 URL,避免手动写路径出错;
  • img-fluid:Bootstrap 的类,让图片自适应页面宽度,在手机上也能正常显示;
  • card shadow-sm:给图表加卡片样式和阴影,让页面更有层次感。

运行测试:

访问http://localhost:5000/report,就能看到两个图表在 Web 页面上展示,和之前本地生成的一样,但现在任何人都能通过浏览器访问。

五、交互功能:学生成绩查询

只展示还不够,还要支持 “用户输入姓名,查询该学生的成绩”。这需要用到 Flask 的表单处理,接收用户输入的姓名,查询数据后返回结果。

1. 修改app.py,添加查询路由

python

# app.py(新增代码,放在report路由后面)
from flask import request  # 新增request对象,用于接收请求数据

# 新增:学生查询路由(支持GET和POST请求)
@app.route('/student/search', methods=['GET', 'POST'])
def student_search():
    # 如果是GET请求(用户刚访问页面),返回空结果的表单
    if request.method == 'GET':
        return render_template('student_search.html')
    
    # 如果是POST请求(用户提交表单),处理查询
    elif request.method == 'POST':
        # 1. 获取用户输入的姓名(从表单中取name为'student_name'的值)
        student_name = request.form.get('student_name', '').strip()
        
        # 2. 验证输入(不能为空)
        if not student_name:
            return render_template('student_search.html', error='请输入学生姓名!')
        
        # 3. 读取数据并查询
        df = pd.read_csv('students_data.csv', encoding='utf-8', dtype={'course_score': int})
        df['grade'] = df['course_score'].apply(
            lambda x: 'A级(90+)' if x >= 90 else ('B级(80-89)' if x >= 80 else 'C级(<80)')
        )
        # 筛选该学生的数据
        student_df = df[df['name'] == student_name]
        
        # 4. 处理查询结果
        if student_df.empty:
            # 没有找到该学生
            return render_template(
                'student_search.html',
                error=f'未找到名为“{student_name}”的学生',
                input_name=student_name  # 回显用户输入的姓名
            )
        else:
            # 找到学生,转换为字典列表
            student_data = student_df.to_dict('records')
            # 计算该学生的总分和平均分
            total_score = student_df['course_score'].sum()
            avg_score = student_df['course_score'].mean().round(1)
            return render_template(
                'student_search.html',
                student=student_data,  # 学生成绩数据
                total_score=total_score,  # 总分
                avg_score=avg_score,  # 平均分
                input_name=student_name  # 回显用户输入的姓名
            )

2. 创建查询模板templates/student_search.html

html

<!-- templates/student_search.html -->
{% extends "base.html" %}

{% block title %}学生查询 - 学生成绩管理系统{% endblock %}

{% block content %}
<div class="row justify-content-center">
    <div class="col-md-8">
        <h2>学生成绩查询</h2>
        
        <!-- 成绩查询表单 -->
        <form method="POST" class="mt-4">
            <div class="input-group mb-3">
                <input type="text" name="student_name" class="form-control" 
                       placeholder="请输入学生姓名(如:小明)" 
                       value="{{ input_name if input_name else '' }}">  <!-- 回显输入 -->
                <button class="btn btn-primary" type="submit">查询</button>
            </div>
            
            <!-- 显示错误信息(如果有) -->
            {% if error %}
            <div class="alert alert-danger" role="alert">
                {{ error }}
            </div>
            {% endif %}
        </form>
        
        <!-- 显示查询结果(如果有) -->
        {% if student %}
        <div class="card mt-4">
            <div class="card-header bg-primary text-white">
                <h5 class="mb-0">
                    {{ input_name }} 的成绩详情
                    <span class="float-end">总分:{{ total_score }} | 平均分:{{ avg_score }}</span>
                </h5>
            </div>
            <div class="card-body">
                <table class="table table-sm mb-0">
                    <thead>
                        <tr>
                            <th>课程名称</th>
                            <th>成绩(分)</th>
                            <th>成绩等级</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for item in student %}
                        <tr>
                            <td>{{ item.course_name }}</td>
                            <td>{{ item.course_score }}</td>
                            <td>
                                {% if item.grade == 'A级(90+)' %}
                                <span class="badge bg-success">{{ item.grade }}</span>
                                {% elif item.grade == 'B级(80-89)' %}
                                <span class="badge bg-warning">{{ item.grade }}</span>
                                {% else %}
                                <span class="badge bg-danger">{{ item.grade }}</span>
                                {% endif %}
                            </td>
                        </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
        {% endif %}
    </div>
</div>
{% endblock %}

关键功能说明:

  • 请求方法methods=['GET', 'POST']表示该路由支持两种请求:GET(访问页面)、POST(提交表单);
  • 表单回显value="{{ input_name if input_name else '' }}"让用户查询失败时,输入框保留之前的内容,不用重新输入;
  • 错误提示:用 Bootstrap 的alert-danger类显示错误信息(如 “未找到学生”),用户体验更好。

运行测试:

  1. 访问http://localhost:5000/student/search
  2. 输入 “小明”,点击查询,会显示小明的课程、成绩、总分和平均分;
  3. 输入 “不存在的名字”,会显示错误提示 “未找到该学生”。

六、完整项目实战:在线学生成绩管理系统

现在,咱们的系统已经有了 4 个核心功能:首页、学生列表、可视化报告、成绩查询。最后,把导航栏和所有功能串联起来,确保用户能通过导航在各个页面间切换(之前的base.html已经做好了导航栏)。

最终项目结构回顾

plaintext

student_web/
├── app.py                  # 主程序(路由、数据处理、图表生成)
├── students_data.csv       # 学生数据(数据源)
├── templates/
│   ├── base.html           # 基础模板(导航栏、底部)
│   ├── index.html          # 首页(系统简介)
│   ├── student_list.html   # 学生列表(所有成绩表格)
│   ├── student_search.html # 成绩查询(交互功能)
│   └── report.html         # 可视化报告(图表展示)
└── static/
    └── images/
        ├── course_avg.png  # 柱状图(自动生成)
        └── grade_pie.png   # 饼图(自动生成)

运行完整系统的步骤

  1. 确保所有文件按上述结构存放;
  2. 安装所有依赖(flask、pandas、matplotlib、seaborn);
  3. 运行python app.py,打开浏览器访问http://localhost:5000/
  4. 通过导航栏切换 “首页→学生列表→成绩查询→可视化报告”,测试所有功能。

七、新手必踩的 5 个坑:避坑指南

Flask 开发虽然简单,但新手容易在 “路径”“请求处理”“静态文件” 上踩坑,总结如下:

坑 1:模板路径错误(找不到 HTML 文件)

python

# 错误示例:模板不在templates文件夹,或文件名写错
return render_template('studentlist.html')  # 错误:正确文件名是student_list.html

解决

  • 模板必须放在templates文件夹(名字不能错,全小写);
  • 渲染时文件名必须和实际一致,包括下划线、大小写(Flask 区分大小写)。

坑 2:静态文件引用错误(图表不显示)

html

<!-- 错误示例:直接写本地路径,Flask无法识别 -->
<img src="static/images/course_avg.png">

解决:用url_for生成静态文件 URL:

html

<img src="{{ url_for('static', filename='images/course_avg.png') }}">

坑 3:请求方法错误(表单提交没反应)

python

# 错误示例:路由只支持GET,无法处理POST请求
@app.route('/student/search')  # 默认methods=['GET']
def student_search():
    if request.method == 'POST':  # 永远不会执行
        pass

解决:路由必须显式指定支持 POST:

python

@app.route('/student/search', methods=['GET', 'POST'])

坑 4:数据读取路径错误(找不到 CSV 文件)

python

# 错误示例:CSV文件不在项目根目录,Pandas找不到
df = pd.read_csv('data/students_data.csv')  # 错误:路径不对

解决

  • 确保students_data.csvapp.py所在的根目录;

  • 或用绝对路径(不推荐,换电脑会失效):

    python

    import os
    csv_path = os.path.join(os.path.dirname(__file__), 'students_data.csv')
    df = pd.read_csv(csv_path)
    

坑 5:调试模式关闭(修改代码不生效)

python

# 错误示例:debug=False,修改代码后需要手动重启服务
app.run(debug=False)

解决:开发时务必开启调试模式:

python

app.run(debug=True)

注意:生产环境(给别人正式使用时)要关闭debug=True,否则有安全风险。

八、小结与下一篇预告

这篇你学到了什么?

  1. Flask 基础:理解路由、视图函数、模板的核心概念,跑通 Web 服务;
  2. 模板开发:用 Jinja2 模板继承减少重复代码,引入 Bootstrap 美化页面;
  3. 数据整合:把 Pandas 本地数据处理逻辑迁移到 Flask,渲染成 Web 表格;
  4. 可视化 Web 化:在 Flask 中生成图表,保存到静态文件夹,在页面展示;
  5. 交互功能:用 request 处理表单提交,实现学生成绩查询,提升用户体验;
  6. 项目实战:构建完整的在线学生成绩管理系统,掌握 Web 项目的文件结构和开发流程。

下一篇预告

今天的 Flask 应用已经能通过浏览器访问,但数据还是存在本地 CSV 文件中,多人同时修改会冲突,而且无法持久化存储新数据(比如新增学生)。下一篇咱们会学 Python 的数据库操作,用SQLite(轻量级数据库,无需安装)替换 CSV 文件,实现数据的增删改查,让 Web 应用真正支持多人协作,成为生产级别的系统。

如果这篇内容帮你搭建了第一个 Flask 应用,欢迎在评论区分享你的系统截图或遇到的问题,咱们一起交流进步~

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。