1、Flask 框架概述
Flask 是 Python 生态中轻量级 Web 微框架 ,以极简设计、高度灵活为核心特色,专注让开发者高效构建 Web 应用,同时保留拓展自由度。它依赖两大核心 Python 库支撑基础能力:
依赖库名称 | Werkzeug | Jinja2 |
---|---|---|
角色定位 | Flask 的 "底层引擎" | Flask 的 "模板渲染中枢" |
核心功能 | 1. 实现 WSGI 规范,处理 HTTP 请求 / 响应全流程 2. 提供路由匹配机制,关联 URL 与视图函数 3. 管理会话、Cookie、请求上下文等基础组件 | 1. 在 HTML 中嵌入 Python 语法(变量、循环、条件等) 2. 动态渲染数据生成 HTML 页面 3. 支持模板继承、过滤器等高级特性 |
技术价值 | 1. 屏蔽底层 HTTP 通信复杂性,让开发者聚焦业务逻辑 2. 提供 URL 规则系统,实现 RESTful API 设计 3. 作为 Web 服务器与应用间的 "翻译官",确保跨平台兼容性 | 1. 分离后端逻辑与前端展示,提升代码可维护性 2. 通过模板复用减少代码冗余 3. 支持安全的内容转义,防止 XSS 攻击 |
典型应用场景 | 1. 构建自定义中间件处理请求预处理 2. 实现非标准 HTTP 协议扩展 3. 开发测试工具模拟 HTTP 请求 | 1. 生成带动态数据的 HTML 页面 2. 构建邮件模板系统 3. 实现网站多主题切换功能 |
拓展能力 | 可与 Flask 插件组合使用,如 Flask - RESTful 增强 API 功能 | 支持自定义过滤器和全局函数,扩展模板语法能力 |
设计理念与适用场景 :
Flask 主打 "轻量起步,按需拓展"------ 核心仅聚焦基础 Web 开发流程,若开发简单工具(如内部数据看板)、小型 API 服务,直接用核心功能即可快速落地;如需用户认证、数据库 ORM、文件上传等复杂能力,可通过 Flask 生态中丰富的 ** 拓展库(如 Flask - Login、Flask - SQLAlchemy )** 灵活补充,适配从原型验证到中大型项目的多样需求 。这种 "微而不弱" 的特性,让它成为开发者快速试错、定制化 Web 开发的优选框
1.1、安装flask模块
bash
pip3 install flask
1.2、创建一个Flask应用
bash
vim aaa.py
python
# 从 flask 库中导入 Flask 类,用于创建 Flask 应用实例
from flask import Flask
# 创建 Flask 应用实例,__name__ 是 Python 内置变量,
# 表示当前模块的名称,Flask 会根据它确定应用的根路径等
app = Flask(__name__)
# 使用 app.route 装饰器,定义根路径('/')对应的视图函数
# 当用户访问网站根目录时,会执行下面的 aaa 函数
@app.route('/')
def aaa():
# 视图函数返回的内容,会作为响应返回给客户端,这里返回简单的字符串 'hello!'
return 'hello!'
# Python 的常见写法,判断当前脚本是否作为主程序运行
if __name__ == '__main__':
# 运行 Flask 应用,设置 host 为 '0.0.0.0' 表示允许外部所有IP访问,
# port 为 5000 是应用监听的端口,debug=True 开启调试模式,
# 调试模式下代码修改后会自动重启,且报错时会显示详细的调试信息
app.run(host='0.0.0.0', port=5000, debug=True)
1.3、运行测试
bash
#关闭防火墙
systemctl stop firewalld
#运行应用
python3 aaa.py


2、Flask路由与视图函数
Flask 里,路由系统是很核心的部分 。简单说,就是把用户在浏览器里访问的网址(URL),和我们写的 Python 函数(视图函数)对应起来 。 咋实现的呢?Flask 用 `@app.route()` 这个装饰器来搞。你给这个装饰器里填个网址规则,比如 `@app.route('/greet')` ,然后下面写个函数,这个函数就是视图函数,它的活儿就是处理用户访问这个网址时的请求,最后返回个响应,像返回网页内容、一串文字啥的,让用户能在浏览器看到结果 。
2.1、动态路由
动态路由使用 <变量名>
语法在 URL 路径中定义变量。这些变量会被捕获并作为参数传递给路由函数
python
# 使用 @app.route 装饰器定义路由规则,
# '/greet/<name>' 表示该路由可以匹配类似 /greet/xxx 的 URL,
# 其中 <name> 是动态路径参数,会将 URL 中对应位置的值传递给视图函数的 name 参数
@app.route('/greet/<name>')
def greet(name):
# 视图函数,接收动态参数 name
# 使用 f-string 格式化字符串,把传入的 name 值拼接到返回内容里,
# 最终返回类似 "Hello, 传入的名称" 这样的字符串给客户端
return f'Hello, {name}!'


2.2、支持HTTP的多种获取方式
python
# 使用 @app.route 装饰器定义路由,这里路由路径是 '/submit'
# methods 参数指定该路由支持的 HTTP 请求方法,
# 这里同时允许 GET 和 POST 请求访问该路由
@app.route('submit', methods=['GET', 'POST'])
def submit():
# 定义视图函数 submit,当客户端访问 '/submit' 路径(且请求方法符合要求)时,
# 这个函数会被执行,返回字符串 '200 OK!' 作为响应内容给客户端
return '200 OK!'

2.3、使用 Jinja2 模板渲染 HTML
Flask 结合 Jinja2 模板引擎来动态生成 HTML 页面。你可以将 HTML 文件与 Python 代码分离,保持应用结构清晰。在 Flask 中,模板文件默认存放在 templates 文件夹中。
创建默认的模板目录:
bash
mkdir templates
在目录下创建index.html模板文件:
html
<!-- 定义 HTML 文档的开始 -->
<html>
<!-- head 部分,用于设置页面的元信息、标题等,不会直接显示在页面主体内容里 -->
<head>
<!-- meta 标签,指定页面内容的字符编码为 UTF-8,确保中文等字符正确显示 -->
<meta charset="UTF-8">
<!-- title 标签,设置浏览器标签栏显示的标题文本 -->
<title>你好zhangsan!</title>
</head>
<!-- body 部分,页面实际显示的内容区域 -->
<body>
<!-- h1 是一级标题标签,这里使用了 Flask 模板引擎(Jinja2)的变量语法 {{ name }},
意味着渲染模板时,会把后端传递的 name 变量值替换到这里显示,实现动态内容展示 -->
<h1>hello,{{ name }}</h1>
</body>
</html>
创建脚本文件:
python
# 从 Flask 框架的核心模块中导入 Flask 类,用于创建 Flask 应用实例
from flask import Flask
# 从 Flask 框架中导入 render_template 函数,用于渲染 HTML 模板文件
from flask import render_template
# 创建 Flask 应用实例,__name__ 是 Python 内置变量,
# 它的值是当前模块的名称,Flask 会根据这个值来确定应用的根路径,
# 从而找到模板文件、静态文件等资源的位置
app = Flask(__name__)
# 使用 @app.route 装饰器为 '/greet/<name>' 路径注册视图函数。
# 这里的 <name> 是动态路由参数,会将 URL 中对应位置的值传递给视图函数的 name 参数
@app.route('/greet/<name>')
def greet(name):
# 调用 render_template 函数,渲染名为 'index.html' 的模板文件。
# 第二个参数 name=name 是将视图函数接收到的 name 参数传递给模板,
# 这样在模板中就可以使用 {{ name }} 来获取并显示这个值
return render_template('index.html', name=name)
# Python 的常见写法,判断当前脚本是否作为主程序运行。
# 当脚本直接被运行时(而不是被其他模块导入时),__name__ 的值会是 '__main__'
if __name__ == '__main__':
# 启动 Flask 应用。设置 host='0.0.0.0' 表示允许外部所有 IP 访问该应用;
# port=5000 指定应用监听的端口为 5000;debug=True 开启调试模式,
# 在调试模式下,代码修改后应用会自动重启,并且遇到错误时会显示详细的调试信息,方便开发调试
app.run(host='0.0.0.0', port=5000, debug=True)
访问测试:

2.4、继承
Flask 支持模板继承和块(Block)功能,这对于构建复杂页面非常有用。你可以在基础模板中定义通用的布局结构,在子模板中覆盖特定的部分。
创建父模板:
bash
cd /root/templates && mkdir base.html
html
<html>
<head>
<!-- 设置网页字符编码为 UTF-8,确保能正确显示中文等多语言字符 -->
<meta charset="UTF-8">
<!--
定义标题的模板块(title block),
子模板可通过继承重写该块来自定义页面标题,
这里默认标题内容是"欢迎来到我的站点!"
-->
<title>{% block title %}欢迎来到我的站点!{% endblock %}</title>
</head>
<body>
<!-- 页面头部区域,一般放置站点标识、导航等内容 -->
<header>
<!-- 页面头部的一级标题,显示"喜羊羊与灰太狼" -->
<h1>喜羊羊与灰太狼</h1>
</header>
<!-- 中间内容容器,子模板可通过填充 content 块,
向这里插入具体页面内容,实现模板继承与内容扩展 -->
<div>
{% block content %}{% endblock %}
</div>
<!-- 页面底部区域,常放置版权信息、联系方式等 -->
<footer>
<!-- 显示版权信息,包含日期"2025-06-13"和站点说明"我的站点!" -->
<p>© 2025-06-13 我的站点!</p>
</footer>
</body>
</html>

创建子模板:
bash
vim index.html
html
{# 继承 base.html 模板,让当前子模板拥有父模板的整体结构,后续可填充/覆盖父模板的区块 #}
{% extends 'base.html' %}
{# 定义 title 区块,用于覆盖父模板中同名的 title 区块,设置页面标题为 Home #}
{% block title %}Home{% endblock %}
{# 定义 content 区块,用于填充父模板中预留的 content 区块 #}
{% block content %}
{# 在 content 区块里添加具体内容,这里是一个二级标题"欢迎来到战场!" #}
<h2>欢迎来到战场!</h2>
{# 结束 content 区块的定义 #}
{% endblock %}

访问测试:
python
from flask import Flask
from flask import render_template
app = Flask(__name__)
# 使用 @app.route 装饰器,为 Flask 应用定义路由规则,这里指定根路径(即网站首页,访问域名/时触发)
@app.route('/')
# 定义视图函数 aaa,当访问上述指定路由(根路径)时,这个函数会被执行
def aaa():
# 调用 render_template 函数,渲染名为 index.html 的模板文件并返回给客户端
# 作用是将模板里的内容(结合可能传入的数据,这里没传额外数据)转换为 HTML 响应返回
return render_template('index.html')
if __name__ == '__main__':
app.run(host='0.0.0.0',port=5000,debug=True)


3、Flask 表单处理与用户输入
Flask 可以通过表单来获取用户输入,常见的做法是结合 FlaskK-WTE 扩展来简化表单处理和验证。
Flask-WTF 为 Flask 提供了一个表单类,你可以在表单类中定义字段和验证规则。
3.1、安装Flask-WTF
bash
pip3 install flask-wtf
3.2、制作一个表单
bash
vim biaodan.py
python
# 从 Flask 框架中导入 Flask 类(用于创建应用实例)、render_template 函数(渲染模板)、request 对象(处理请求)
from flask import Flask, render_template, request
# 从 flask_wtf 库中导入 FlaskForm 类,用于创建表单类
from flask_wtf import FlaskForm
# 从 wtforms 库中导入 StringField 类,用于创建字符串输入框表单字段
from wtforms import StringField
# 从 wtforms.validators 中导入 DataRequired 验证器,用于要求字段必须填写内容
from wtforms.validators import DataRequired
# 创建 Flask 应用实例,__name__ 会根据当前模块名称动态赋值,帮助 Flask 找到模板和静态文件等资源
app = Flask(__name__)
# 设置 Flask 应用的密钥,用于会话安全、表单 CSRF 保护等,这里简单设为's3cr3t'(实际应用应设更复杂安全的密钥)
app.secret_key ='s3cr3t'
# 定义表单类 NameForm,继承自 FlaskForm,用于创建包含特定字段的表单
class NameForm(FlaskForm):
# 创建一个字符串输入框字段,名称为'name',在页面上显示的标签是'Name',并且要求该字段必须填写(通过 DataRequired 验证器)
name = StringField('Name', validators=[DataRequired()])
# 定义路由装饰器,将根路径'/'与视图函数 index 关联,允许处理 GET 和 POST 请求
@app.route('/', methods=['GET', 'POST'])
def index():
# 创建 NameForm 表单实例,用于后续处理用户提交或展示表单
form = NameForm()
# 检查表单是否通过验证且是通过提交(POST)方式触发,validate_on_submit 会先检查请求方法是否是 POST,再验证表单字段
if form.validate_on_submit():
# 如果验证通过,获取表单中 name 字段用户输入的数据,拼接成问候语并返回
return f'Hello, {form.name.data}!'
# 如果不是通过提交触发(比如初始访问是 GET 请求),渲染 index.html 模板,并把表单实例传给模板,方便在页面上显示表单
return render_template('index.html', form=form)
# Python 脚本入口判断,当脚本被直接运行时执行下面代码
if __name__ == '__main__':
# 原本的启动方式,调试模式开启(调试模式下代码改动会自动重启应用,且报错信息更详细,不过仅适合开发环境),这里被注释了
# app.run(debug=True)
# 实际使用的启动方式,设置主机为 '0.0.0.0'(允许外部设备访问该应用,比如同一局域网内的其他机器),端口为 5000,开启调试模式
app.run(host='0.0.0.0', port=5000, debug=True)
index.html模板:
bash
vim index.html
html
<!-- 定义 HTML 文档,语言为英文("en" 表示 English ) -->
<html lang="en">
<head>
<!-- 设置网页的字符编码为 UTF-8,确保能正确显示各种字符,避免乱码 -->
<meta charset="UTF-8">
<!-- 设置网页标题,在浏览器标签上显示为 "Flask Form" -->
<title>Flask Form</title>
</head>
<body>
<!-- 一级标题,提示用户输入姓名 -->
<h1>Enter your name:</h1>
<!-- 定义表单,提交数据时使用 POST 方法,和 Flask 后端路由 @app.route('/', methods=['GET', 'POST']) 对应,用于将用户输入的数据提交到后端 -->
<form method="POST">
<!-- 渲染 CSRF 令牌,这是 flask_wtf 提供的安全机制,用于防止跨站请求伪造攻击。后端通过设置 secret_key 配合,表单必须包含这个令牌,后端才会认可表单提交 -->
{{ form.csrf_token }}
<!-- 标签,关联下方的 name 输入框,点击标签也能聚焦到输入框,提升用户体验,"for" 属性值和表单字段的 id 对应(由 Flask-WTF 自动处理) -->
<label for="name">Name:</label>
<!-- 渲染 NameForm 中的 name 字符串输入框字段,会生成对应的 HTML input 元素,用户在这里输入姓名。<br><br> 是换行符,让输入框和按钮之间空出两行间距 -->
{{ form.name() }}<br><br>
<!-- 提交按钮,用户点击后触发表单的提交操作,将数据发送到后端 Flask 应用的对应路由进行处理 -->
<button type="submit">Submit</button>
</form>
<!-- Jinja2 模板的条件判断语法,检查表单中 name 字段是否有用户提交的数据(即是否经过了有效提交且通过验证) -->
{% if form.name.data %}
<!-- 如果有提交的姓名数据,就用二级标题显示问候语,展示用户输入的姓名,实现前后端数据交互后的反馈 -->
<h2>Hello, {{ form.name.data }}!</h2>
<!-- 条件判断结束的语法标记 -->
{% endif %}
</body>
</html>

验证:

4、项目
路径 | 类型 | 作用说明 |
---|---|---|
/my_flask_app |
目录 | 项目根目录,包裹整个 Flask 应用的所有文件,是项目的容器。 |
/my_flask_app/app |
目录 | 应用核心目录,集中存放应用的主要逻辑(路由、表单、初始化等),便于模块化管理。 |
/templates |
目录 | 存放 HTML 模板文件,Flask 会从这里读取模板,结合视图函数渲染动态页面。 |
index.html |
文件 | 示例 HTML 模板,实际项目中会有更多页面模板,用于展示页面内容、嵌入 Jinja2 语法。 |
/static |
目录 | 静态资源存储地,浏览器可直接访问这里的文件(如 CSS 美化样式、JS 交互脚本 )。 |
/css |
目录 | 专门存放 CSS 样式文件,管理页面视觉效果(布局、颜色、字体等 )。 |
/js |
目录 | 存放 JavaScript 文件,实现前端交互逻辑(如按钮点击、数据异步请求 )。 |
__init__.py |
文件 | 标记 app 为 Python 包,在此创建 Flask 应用实例、配置扩展(如数据库、密钥 )。 |
routes.py |
文件 | 定义 URL 路由规则和对应的视图函数,决定用户访问不同地址时执行的逻辑与返回内容。 |
forms.py |
文件 | 若使用 Flask-WTF 等表单扩展,在此定义表单类(包含字段、验证规则 ),方便前后端表单交互。 |
run.py |
文件 | 应用启动入口,编写代码运行 Flask 开发服务器,让应用可被外部访问调试。 |
4.1、栗子:
项目结构:
路径 | 说明 |
---|---|
/simple_blog |
项目的根目录,作为整个博客应用的容器,包裹所有相关文件 |
/app |
应用核心目录,存放博客应用的主要代码逻辑,实现功能封装与模块化管理 |
/templates |
模板目录,用于存放 HTML 模板文件(如 index.html ),借助 Flask 等框架渲染动态页面,分离前端展示与后端逻辑 |
index.html |
示例 HTML 模板文件,可作为博客首页等页面的基础,后续可扩展布局、内容展示等 |
app.py |
核心代码文件,通常用于编写 Flask 应用的初始化、路由定义、业务逻辑等,是应用的 "大脑",控制请求响应流程 |
app.py:
bash
vim app.py
python
# 从 flask 框架中导入 Flask 类(用于创建应用实例)、render_template 函数(用于渲染模板)、request 对象(用于处理请求数据)
from flask import Flask, render_template, request
# 创建 Flask 应用实例,__name__ 代表当前 Python 模块的名称,Flask 会根据它确定应用相关资源(如模板、静态文件)的路径
app = Flask(__name__)
# 定义一个列表,用于存储所有的文章数据,每个元素是包含 'title'(标题)和 'content'(内容)的字典
posts = []
# 定义路由装饰器,将根路径('/')与视图函数 index 绑定,同时允许处理 GET 和 POST 两种请求方法
@app.route('/', methods=['GET', 'POST'])
def index():
# 判断请求方法是否为 POST,如果是,说明是用户提交表单数据
if request.method == 'POST':
# 从请求的表单数据中获取标题,键为 'title',对应前端表单里的名称
title = request.form['title']
# 从请求的表单数据中获取文章内容,键为 'content',对应前端表单里的名称
content = request.form['content']
# 将获取到的标题和内容整理成字典,添加到 posts 列表中,实现文章数据的存储
posts.append({'title': title, 'content': content})
# 渲染名为 'index.html' 的模板文件,把 posts 列表传递给模板,这样模板中就能使用该数据展示文章,最后返回渲染后的结果给客户端
return render_template('index.html', posts=posts)
# 判断当前脚本是否是作为主程序运行(而不是被其他模块导入)
if __name__ == '__main__':
# 启动 Flask 应用,设置 host 为 '0.0.0.0' 表示允许外部所有 IP 访问,端口为 5000,debug=True 开启调试模式,代码修改后会自动重启,且能显示错误详情
app.run(host='0.0.0.0', port=5000, debug=True)

index.html:
bash
vim index.html
html
<html lang="en"> <!-- 定义 HTML 文档的语言为英语 -->
<head>
<meta charset="UTF-8"> <!-- 设置字符编码为 UTF-8,确保正确显示各种字符 -->
<title>Simple Blog</title> <!-- 设置网页标题,会在浏览器标签栏显示 -->
</head>
<body>
<h1>Welcome to the Blog!</h1> <!-- 一级标题,显示博客欢迎语 -->
<h2>Submit a Post</h2> <!-- 二级标题,标识提交文章的区域 -->
<form method="POST"> <!-- 定义一个 POST 方法的表单,用于提交文章数据 -->
<label for="title">Title:</label> <!-- 关联"title"输入框的标签,说明要填写标题 -->
<input type="text" id="title" name="title" required><br><br>
<!--
文本类型输入框,id 和 name 都为"title",required 表示该字段为必填项
填写的标题内容会随表单提交,<br> 用于换行
-->
<label for="content">Content:</label> <!-- 关联"content"文本域的标签,说明要填写内容 -->
<textarea id="content" name="content" required></textarea><br><br>
<!--
多行文本域,id 和 name 都为"content",required 表示必填
用于填写文章具体内容,填写的内容会随表单提交,<br> 用于换行
-->
<button type="submit">Submit</button> <!-- 提交按钮,点击后提交表单数据 -->
</form>
<h2>Posts:</h2> <!-- 二级标题,标识展示文章列表的区域 -->
<ul> <!-- 无序列表,用于包裹文章列表项 -->
{% for post in posts %} <!-- Jinja2 模板的 for 循环,遍历名为 posts 的列表数据 -->
<li> <!-- 列表项,每个文章对应一个列表项 -->
<h3>{{ post.title }}</h3> <!-- 展示文章的标题,从 post 对象的 title 属性获取内容 -->
<p>{{ post.content }}</p> <!-- 展示文章的内容,从 post 对象的 content 属性获取内容 -->
</li>
{% endfor %} <!-- 结束 for 循环 -->
</ul>
</body>
</html>
验证:
