Flask 入门:Python Web 框架快速上手指南
欢迎来到 Python Web 开发的世界!如果您正在寻找一个轻量级、灵活且易于上手的 Web 框架,那么 Flask 绝对是您的理想选择。与一些大型的“全栈”框架(如 Django)不同,Flask 被称为“微框架”,因为它只提供 Web 开发的核心功能,而将数据库、认证、表单验证等功能交给扩展来处理。这种设计理念使得 Flask 更加灵活,开发者可以根据自己的需求自由选择和组合工具。
本文将带您从零开始,一步步深入 Flask 的世界,学习如何搭建开发环境、构建第一个应用、处理请求与响应、使用模板、集成静态文件、处理表单数据,并初步了解数据存储的概念。无论您是刚刚接触 Web 开发的 Python 爱好者,还是希望尝试一个新框架的经验丰富的开发者,本文都将为您提供扎实的入门基础。
文章结构:
- Flask 简介: 什么是 Flask?为什么选择 Flask?
- 准备工作: 安装 Python、pip 和创建虚拟环境。
- 安装 Flask: 使用 pip 安装 Flask。
- 你的第一个 Flask 应用: 编写并运行一个简单的 “Hello, World!” 应用。
- 理解核心概念: 应用实例、路由、视图函数。
- 处理不同 URL 和 HTTP 方法: 创建多个路由,处理 GET 和 POST 请求。
- 动态 URL (URL 变量): 如何从 URL 中捕获变量。
- 使用模板: 告别硬编码 HTML,引入 Jinja2 模板引擎。
- 创建模板文件
- 渲染模板并传递数据
- 模板继承简介
- 静态文件: 如何在 Flask 应用中提供 CSS、JavaScript 和图片。
- 处理表单数据: 从用户提交的表单中获取数据。
- 创建 HTML 表单
- 在 Flask 中接收和处理表单数据
- 重定向与
url_for
- 数据存储简介: 为什么需要数据库?Flask 如何与数据库集成(概念性介绍)。
- 会话 (Sessions): 在请求之间存储用户特定信息。
- 错误处理: 定制 404 等错误页面。
- 组织大型应用 (蓝图简介): 如何使用蓝图使应用结构更清晰。
- 下一步: 部署、测试、更高级功能和常用扩展。
- 总结: 回顾所学,鼓励实践。
好,让我们开始这段 Flask 学习之旅吧!
1. Flask 简介:什么是 Flask?为什么选择 Flask?
什么是 Flask?
Flask 是一个使用 Python 编写的轻量级 Web 服务器网关接口(WSGI)Web 应用框架。它基于 Werkzeug WSGI 工具集和 Jinja2 模板引擎。Flask 的目标是保持核心功能的简洁和灵活,通过扩展来增加额外功能。这使得开发者可以根据项目需求选择合适的工具,而不是被框架强制使用特定的库或方法。
为什么选择 Flask?
- 轻量级和简洁: Flask 的核心非常小巧,学习曲线相对平缓。您可以在短时间内理解其基本工作原理。
- 灵活和可扩展: Flask 不做过多限制,您可以自由选择数据库、模板引擎(尽管默认是 Jinja2)、ORM(对象关系映射)工具等。社区提供了大量的 Flask 扩展,可以轻松集成各种功能,如用户认证、数据库操作、表单处理、缓存等。
- 易于上手: 对于有 Python 基础的开发者来说,学习 Flask 非常容易。它的 API 设计直观,文档清晰。
- 非常适合小型到中型应用: Flask 在构建简单的 Web 服务、API 或者原型开发时表现出色。当然,通过良好的架构设计和合理使用扩展,Flask 也能用于构建大型复杂应用。
- 活跃的社区: Flask 拥有一个活跃的开发者社区,您可以轻松找到教程、文档和寻求帮助。
如果您追求快速启动、高度定制化,或者您的项目规模适中,不希望被框架的“全家桶”所束缚,那么 Flask 是一个非常好的选择。
2. 准备工作:安装 Python、pip 和创建虚拟环境
在开始之前,请确保您的系统已经安装了 Python 和 pip(Python 的包管理器)。通常安装 Python 时会自动安装 pip。
- 安装 Python: 访问 python.org 下载并安装最新版本的 Python 3。在安装过程中,请确保勾选“Add Python to PATH”(将 Python 添加到系统路径)。
- 验证安装: 打开终端或命令提示符,输入以下命令验证 Python 和 pip 是否安装成功:
bash
python --version
pip --version
如果显示版本号,则说明安装成功。
创建和使用虚拟环境 (Virtual Environments)
强烈建议在进行任何 Python 项目开发时使用虚拟环境。虚拟环境可以为您的项目创建一个独立的 Python 环境,所有安装的库都将在这个环境中,不会影响系统全局的 Python 安装或其他项目。这有助于避免不同项目之间库版本冲突的问题。
-
创建虚拟环境: 打开终端或命令提示符,切换到您希望创建项目的目录。然后执行以下命令创建一个名为
venv
的虚拟环境(名称可以自定义):- 在 macOS/Linux 上:
bash
python3 -m venv venv - 在 Windows 上:
bash
python -m venv venv
这会在当前目录下创建一个名为venv
的文件夹,其中包含独立的 Python 解释器和 pip。
- 在 macOS/Linux 上:
-
激活虚拟环境: 在开始项目开发或安装库之前,需要激活虚拟环境。
- 在 macOS/Linux 上:
bash
source venv/bin/activate - 在 Windows 命令提示符 (cmd) 上:
bash
venv\Scripts\activate - 在 Windows PowerShell 上:
powershell
.\venv\Scripts\Activate.ps1
激活成功后,您会看到终端提示符前面多了一个(venv)
字样,表示您当前正处于venv
虚拟环境中。
- 在 macOS/Linux 上:
-
退出虚拟环境: 当您完成项目开发或需要切换到其他环境时,可以简单地输入
deactivate
命令来退出当前虚拟环境。
请注意: 在激活虚拟环境后,使用 pip install
命令安装的任何库都将只安装到当前激活的虚拟环境中。
3. 安装 Flask
虚拟环境激活后,安装 Flask 就非常简单了。使用 pip 执行以下命令:
bash
pip install Flask
Pip 会自动下载并安装 Flask 及其依赖项(主要是 Werkzeug 和 Jinja2)。安装完成后,您可以输入 pip list
查看当前虚拟环境中安装的所有库,应该能看到 Flask、Werkzeug、Jinja2、MarkupSafe 和 ItsDangerous。
至此,您的开发环境已经准备就绪,可以开始编写第一个 Flask 应用了!
4. 你的第一个 Flask 应用:”Hello, World!”
按照惯例,我们的第一个 Web 应用将是一个经典的 “Hello, World!”。
创建一个名为 app.py
的 Python 文件,并将以下代码复制进去:
“`python
app.py
from flask import Flask
创建一个 Flask 应用实例
name 是一个特殊的 Python 变量,它的值取决于运行哪个模块。
如果它是直接运行的模块,name 的值是 ‘main‘。
Flask 需要知道在哪里查找资源,name 参数告诉 Flask 应用的根目录。
app = Flask(name)
使用装饰器 @app.route() 定义一个 URL 路由
当用户访问应用程序的根 URL (‘/’) 时,将执行下面的 home() 函数
@app.route(‘/’)
def home():
“””处理根 URL(‘/’)的请求,返回一个简单的字符串响应.”””
return ‘Hello, World! Welcome to my first Flask app!’
确保此脚本直接运行时才启动开发服务器
if name == ‘main‘: 是 Python 的标准用法,
保证这段代码只在直接运行 app.py 时执行,而不是被其他文件导入时执行。
debug=True 开启调试模式,开发过程中非常有用,代码修改后服务器会自动重载,
并且在发生错误时提供详细的错误信息。但在生产环境中必须关闭调试模式!
if name == ‘main‘:
app.run(debug=True)
“`
代码解释:
from flask import Flask
: 从flask
库中导入Flask
类。app = Flask(__name__)
: 创建 Flask 应用程序的实例。__name__
是必需的,它可以帮助 Flask 确定应用程序的根目录,以便找到资源文件(如模板和静态文件)。@app.route('/')
: 这是一个装饰器,它将下面的函数home()
绑定到 URL/
。当用户在浏览器中访问应用程序的根地址(例如http://127.0.0.1:5000/
)时,Flask 会执行home()
函数。def home():
: 这是一个视图函数。当对应的 URL 被访问时,Flask 会调用这个函数。函数返回的值就是发送给用户的响应。在这里,它返回一个简单的字符串。if __name__ == '__main__':
: 这是一个标准的 Python 惯用法,确保app.run()
只在直接运行app.py
这个文件时执行。app.run(debug=True)
: 启动 Flask 的开发服务器。debug=True
开启了调试模式,这在开发过程中非常方便,它可以让服务器在代码修改后自动重载,并在发生错误时提供详细的错误信息。请记住,在生产环境中永远不要开启调试模式,因为它可能会暴露敏感信息。
运行应用程序:
- 确保您已经激活了之前创建的虚拟环境。
- 在终端或命令提示符中,切换到存放
app.py
文件的目录。 - 执行以下命令:
bash
python app.py
您会看到类似以下的输出:
* Serving Flask app 'app' (lazy loading)
* Environment: development
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: XXX-XXX-XXX
这表示 Flask 开发服务器已经在本地启动,监听端口 5000。
现在,打开您的浏览器,访问 http://127.0.0.1:5000/
。您应该能看到页面上显示 “Hello, World! Welcome to my first Flask app!”。
恭喜您!您已经成功运行了您的第一个 Flask 应用!
5. 理解核心概念:应用实例、路由、视图函数
在上面的 “Hello, World!” 例子中,我们已经接触了 Flask 的几个核心概念:
- 应用实例 (
app = Flask(__name__)
): 这是整个 Flask 应用的中心。您会在这个实例上配置应用、注册路由、处理请求等。它是您创建的每个 Flask 应用的起点。 - 路由 (
@app.route('/')
): 路由是将特定的 URL 模式映射到处理该 URL 的函数的过程。在 Flask 中,通常使用@app.route()
装饰器来定义路由。当 Flask 接收到针对某个 URL 的请求时,它会查找与之匹配的路由,并调用相应的视图函数。 - 视图函数 (
def home():
): 视图函数是用来处理特定路由请求的 Python 函数。它的职责是处理请求逻辑(如读取数据、处理表单等)并返回一个响应,这个响应通常是 HTML 页面、JSON 数据或简单的字符串。
6. 处理不同 URL 和 HTTP 方法
一个真实的 Web 应用通常需要处理多个不同的 URL。我们可以在 app.py
文件中添加更多路由和视图函数。
修改 app.py
文件如下:
“`python
from flask import Flask
app = Flask(name)
@app.route(‘/’)
def home():
return ‘Welcome to the Home Page!’
@app.route(‘/about’)
def about():
return ‘This is the About page of my Flask app.’
@app.route(‘/contact’)
def contact():
return ‘Contact us at [email protected]’
处理特定的 HTTP 方法
默认路由只接受 GET 请求
可以通过 methods 参数指定接受的 HTTP 方法
@app.route(‘/submit’, methods=[‘GET’, ‘POST’])
def submit():
if request.method == ‘POST’:
# 处理 POST 请求的逻辑 (稍后在表单处理中详细介绍)
return ‘Received a POST request’
else:
# 处理 GET 请求的逻辑
return ‘Send a POST request to this URL’
导入 request 对象以便访问请求数据
from flask import request
… (前面的代码) …
@app.route(‘/submit’, methods=[‘GET’, ‘POST’])
def submit():
if request.method == ‘POST’:
# 在处理表单时我们会使用 request.form 来获取 POST 数据
# 现在只是简单返回一个确认信息
return ‘Received a POST request for submission.’
else:
return ‘This page is for submitting data via POST.’
if name == ‘main‘:
app.run(debug=True)
“`
重新运行 python app.py
。现在您可以访问:
http://127.0.0.1:5000/
http://127.0.0.1:5000/about
http://127.0.0.1:5000/contact
http://127.0.0.1:5000/submit
(使用 GET 请求访问会显示 “This page is for submitting data via POST.”)
HTTP 方法:
Web 请求使用不同的 HTTP 方法,最常见的是 GET 和 POST。
- GET: 用于从服务器获取数据。例如,当您在浏览器中直接输入 URL 访问一个页面时,使用的是 GET 请求。
- POST: 通常用于向服务器提交数据,例如提交表单。
默认情况下,@app.route()
装饰器只响应 GET 请求。通过设置 methods=['GET', 'POST']
,您可以让同一个视图函数处理不同类型的请求。在函数内部,您可以使用 request.method
来判断当前请求是 GET 还是 POST,并执行相应的逻辑。request
对象是 Flask 提供的一个全局对象,用于访问当前请求的所有信息(如方法、表单数据、URL 参数等)。
7. 动态 URL (URL 变量)
很多时候,URL 中会包含一些变量,例如用户 ID、文章 ID 等。Flask 允许您在路由规则中定义这样的变量。
修改 app.py
文件,添加一个处理用户信息的路由:
“`python
… (前面的代码) …
@app.route(‘/user/
def show_user_profile(username):
“””根据用户名显示用户资料页.”””
# username 变量会从 URL 中捕获并作为参数传递给函数
return f’User Profile: {username}’
@app.route(‘/post/
def show_post(post_id):
“””根据文章 ID 显示文章详情页.”””
#
# Flask 会自动将其转换为 int 类型并传递给函数
return f’Post ID: {post_id}’
… (if name == ‘main‘: …)
“`
重新运行 python app.py
。现在您可以访问:
http://127.0.0.1:5000/user/Alice
-> 显示 “User Profile: Alice”http://127.0.0.1:5000/user/Bob
-> 显示 “User Profile: Bob”http://127.0.0.1:5000/post/1
-> 显示 “Post ID: 1”http://127.0.0.1:5000/post/123
-> 显示 “Post ID: 123”http://127.0.0.1:5000/post/abc
-> 会返回 404 Not Found 错误,因为 ‘abc’ 不是整数,与<int:post_id>
不匹配。
URL 变量和转换器:
在路由规则中,使用 <variable_name>
语法来定义 URL 变量。Flask 会捕获 URL 中对应位置的值,并将其作为关键字参数传递给视图函数。
您还可以指定变量的类型,称为转换器 (Converters)。常用的转换器包括:
<string:variable>
(默认): 接受任何不包含斜杠的字符串。<int:variable>
: 接受整数。<float:variable>
: 接受浮点数。<path:variable>
: 接受包含斜杠的字符串,通常用于匹配文件路径。<uuid:variable>
: 接受 UUID 字符串。
使用转换器可以帮助 Flask 进行基本的 URL 验证,确保捕获到的变量符合预期类型。
8. 使用模板
直接在视图函数中返回 HTML 字符串对于简单的应用来说尚可,但对于复杂的页面布局和动态内容,这种方式将变得难以维护。我们需要将页面的结构(HTML)与数据和逻辑(Python 视图函数)分离开来。这就是模板引擎的作用。
Flask 默认集成了 Jinja2 模板引擎,它非常强大且易于使用。
步骤:
- 创建
templates
文件夹: 在与app.py
同级的目录下创建一个名为templates
的文件夹。Flask 默认会在这个文件夹中查找模板文件。 -
创建模板文件: 在
templates
文件夹中创建一个名为index.html
的文件。“`html
<!DOCTYPE html>
{{ title }} {# 使用 {{ }} 语法插入变量 #}
Hello, {{ name }}!
{# 变量 name 的值会在这里显示 #}
Welcome to our Flask application.
{# Jinja2 控制结构示例 #} {% if users %} {# 使用 {% %} 语法执行控制结构 #} <h2>Users:</h2> <ul> {% for user in users %} {# 循环遍历 users 列表 #} <li>{{ user }}</li> {% endfor %} </ul> {% else %} <p>No users found.</p> {% endif %} <p>Current Year: {{ current_year }}</p> {# 传递数字变量 #}
``
{{ variable }}
在 Jinja2 模板中:
*用于输出变量的值。
{% statement %}
*用于执行控制结构,如
if语句、
for循环等。
{# comment #}` 用于添加注释。
* -
在 Flask 中渲染模板: 修改
app.py
,导入render_template
函数,并修改视图函数以使用模板。“`python
from flask import Flask, render_template # 导入 render_templateapp = Flask(name)
@app.route(‘/’)
def index():
“””渲染首页模板.”””
# 定义要传递给模板的数据
page_title = ‘Home Page’
user_name = ‘Guest’
user_list = [‘Alice’, ‘Bob’, ‘Charlie’]
current_year = 2023 # 示例数据# 调用 render_template 函数,指定模板文件名和要传递的变量 # 变量以关键字参数的形式传递,键名即为模板中使用的变量名 return render_template('index.html', title=page_title, name=user_name, users=user_list, current_year=current_year)
… (其他路由和 if name == ‘main‘: …)
“`
重新运行 python app.py
,访问 http://127.0.0.1:5000/
。您将看到由 index.html
模板渲染生成的页面,其中变量 title
、name
、users
和 current_year
的值已经被替换。
模板继承简介:
对于大型应用,不同的页面通常会有很多共同的部分(如头部、导航、底部)。模板继承是 Jinja2 的一个强大特性,可以帮助您避免在每个模板文件中重复编写这些共同部分。
-
创建一个
base.html
基础模板:
“`html
<!DOCTYPE html>
{% block title %}My Flask App{% endblock %} {# 定义一个名为 title 的块 #}
{# 可以添加一些公共的 CSS 或 JS 链接 #}
{% block page_header %}Welcome{% endblock %}
{# 定义页面标题块 #}
<main> {% block content %}{% endblock %} {# 定义一个名为 content 的块,用于插入具体页面的内容 #} </main> <footer> <p>© {{ current_year }} My Flask App</p> </footer>
``
{% block block_name %}{% endblock %}` 定义了一个可以被子模板覆盖的区域。 -
修改
index.html
和其他模板,使其继承base.html
:
“`html
{% extends “base.html” %} {# 声明继承 base.html #}{% block title %}Home{% endblock %} {# 覆盖 base.html 中的 title 块 #}
{% block page_header %}Home Page{% endblock %} {# 覆盖 base.html 中的 page_header 块 #}
{% block content %} {# 覆盖 base.html 中的 content 块 #}
This is the main content for the home page.
<h2>Users:</h2> <ul> {% for user in users %} <li>{{ user }}</li> {% endfor %} </ul>
{% endblock %}
html
{% extends “base.html” %}{% block title %}About{% endblock %}
{% block page_header %}About Us{% endblock %}
{% block content %}
Learn more about our amazing Flask app.
{% endblock %}
“` -
在
app.py
中创建渲染about.html
的路由(如果需要):
“`python
# … (前面的导入和 app 实例) …@app.route(‘/’)
def index():
# … (传递给 index.html 的变量) …
user_list = [‘Alice’, ‘Bob’, ‘Charlie’]
current_year = 2023
return render_template(‘index.html’,
title=’Home’, # 可以在这里传递值给 base.html 中的 block
name=’Guest’,
users=user_list,
current_year=current_year)@app.route(‘/about’)
def about():
current_year = 2023 # 如果 base.html 需要这个变量
return render_template(‘about.html’, current_year=current_year) # 渲染 about.html… (其他路由和 if name == ‘main‘: …)
``
base.html
注意:如果中的 block 依赖于某个变量(如
current_year),那么在渲染继承它的子模板时,也需要将这个变量传递给
render_template函数,即使它只在
base.html` 中使用。
使用模板继承可以极大地提高代码的复用性和可维护性。
9. 静态文件
Web 应用通常需要提供静态文件,如 CSS 样式表、JavaScript 脚本、图片、字体文件等。Flask 也为此提供了方便的支持。
- 创建
static
文件夹: 在与app.py
和templates
文件夹同级的目录下创建一个名为static
的文件夹。Flask 默认会在这里查找静态文件。 -
创建静态文件: 在
static
文件夹中创建一个子文件夹,例如css
,并在其中创建一个style.css
文件。“`css
/ static/css/style.css /
body {
font-family: sans-serif;
margin: 20px;
background-color: #f4f4f4;
}h1, h2 {
color: #333;
}nav a {
margin-right: 10px;
text-decoration: none;
}footer {
margin-top: 20px;
padding-top: 10px;
border-top: 1px solid #ccc;
font-size: 0.9em;
color: #666;
}
“` -
在模板中链接静态文件: 在您的模板文件(例如
base.html
或index.html
的<head>
部分)中,使用url_for()
函数来生成静态文件的 URL。html
<!-- templates/base.html (修改 head 部分) -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My Flask App{% endblock %}</title>
{# 使用 url_for 生成静态文件 URL #}
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
{# ... (body content) ... #}
</body>
</html>
url_for()
函数:
url_for()
是 Flask 提供的一个非常实用的函数,用于为指定的视图函数或静态文件生成 URL。它的第一个参数是“端点名称”(Endpoint Name)。
- 对于视图函数,端点名称通常是视图函数的名称(例如,
home
视图函数的端点就是'home'
)。url_for('home')
会生成/
。如果您有带有参数的路由,例如@app.route('/user/<username>')
对应的show_user_profile
函数,可以使用url_for('show_user_profile', username='Alice')
来生成/user/Alice
。 - 对于静态文件,端点名称是
'static'
。url_for('static', filename='css/style.css')
会生成/static/css/style.css
。Flask 开发服务器会自动处理/static/
路径下的请求,并将static
文件夹中的文件作为响应返回。
使用 url_for()
而不是硬编码 URL 有两个好处:
1. 路由规则变化时,链接会自动更新: 如果您修改了 @app.route('/')
到 @app.route('/index')
,只需要在 app.py
中修改,模板中的 url_for('index')
仍然会生成正确的 /index
URL。
2. 更好地处理 URL 前缀等情况: 在更复杂的部署场景中,应用可能有 URL 前缀,url_for
可以正确地处理这些情况。
重新运行应用,访问首页,您应该能看到页面应用了 style.css
中的样式。
10. 处理表单数据
Web 应用与用户交互最常见的方式之一就是通过表单。用户在表单中输入信息,然后提交到服务器进行处理。
步骤:
-
创建包含表单的模板: 创建一个
form.html
模板文件。“`html
{% extends “base.html” %}{% block title %}Contact Form{% endblock %}
{% block page_header %}Contact Us{% endblock %}
{% block content %}
Please fill out the form below:
<form method="POST" action="{{ url_for('submit_form') }}"> {# 表单提交到 submit_form 路由 #} <div> <label for="name">Name:</label><br> <input type="text" id="name" name="name" required> </div> <br> <div> <label for="email">Email:</label><br> <input type="email" id="email" name="email" required> </div> <br> <div> <label for="message">Message:</label><br> <textarea id="message" name="message" rows="4" cols="50" required></textarea> </div> <br> <div> <button type="submit">Submit</button> </div> </form>
{% endblock %}
``
* -
创建处理表单的路由: 修改
app.py
,添加一个显示表单的路由和一个处理表单提交的路由。“`python
from flask import Flask, render_template, request, redirect, url_for # 导入 request, redirect, url_forapp = Flask(name)
… (其他路由,如 home, about, user, post) …
@app.route(‘/contact/form’, methods=[‘GET’])
def contact_form():
“””显示联系表单.”””
current_year = 2023
return render_template(‘form.html’, current_year=current_year) # 渲染表单模板@app.route(‘/contact/submit’, methods=[‘POST’])
def submit_form():
“””处理表单提交.”””
if request.method == ‘POST’:
# 从 request.form 中获取表单数据
# request.form 是一个包含表单字段和值的字典状对象
name = request.form.get(‘name’) # 使用 .get() 更安全,如果键不存在不会抛出错误
email = request.form.get(’email’)
message = request.form.get(‘message’)# 可以在这里处理数据,例如保存到数据库、发送邮件等 print(f"Received submission:\nName: {name}\nEmail: {email}\nMessage: {message}") # 处理完成后,通常会重定向到另一个页面,防止用户刷新页面重复提交 # url_for('success_page') 会生成 'success_page' 路由对应的 URL return redirect(url_for('submission_success')) else: # 如果不是 POST 请求访问这个 URL,可以返回错误或重定向 # 虽然我们methods=['POST']限制了只能POST,但直接访问这个URL(GET)会收到Method Not Allowed错误 # 如果想更友好处理,可以在这里添加逻辑 return "Method Not Allowed", 405 # Flask 默认会处理,这里仅作示例
@app.route(‘/success’)
def submission_success():
“””显示提交成功页面.”””
current_year = 2023
return render_template(‘success.html’, current_year=current_year)… (if name == ‘main‘: …)
“`
-
创建提交成功页面模板: 创建
success.html
文件。“`html
{% extends “base.html” %}{% block title %}Success{% endblock %}
{% block page_header %}Submission Successful{% endblock %}
{% block content %}
Thank you for your submission! We will get back to you shortly.
{% endblock %}
“`
重新运行应用:
- 访问
http://127.0.0.1:5000/contact/form
查看表单。 - 填写表单并提交。
- 您应该会被重定向到
http://127.0.0.1:5000/success
页面。 - 查看终端输出,您会看到打印出来的表单数据。
request
对象和 request.form
:
当用户通过 POST 方法提交表单时,表单数据会包含在请求体中。Flask 的 request
对象提供了一个 form
属性,它是一个类似字典的对象,可以通过字段的 name
属性来访问提交的数据。
request.form['field_name']
: 如果字段不存在,会抛出KeyError
。request.form.get('field_name')
: 如果字段不存在,会返回None
,更安全。您还可以提供一个默认值作为第二个参数,例如request.form.get('field_name', 'default_value')
。
redirect()
和 url_for()
:
处理完 POST 请求后,通常的最佳实践是执行重定向(Post/Redirect/Get 模式)。这样做可以防止用户刷新页面时浏览器提示重新提交表单,也可以防止用户点击后退按钮回到表单页面时意外地再次提交。redirect()
函数用于生成一个重定向响应,它接受一个 URL 作为参数。结合 url_for()
来生成目标 URL 是一个良好的习惯。
11. 数据存储简介:为什么需要数据库?
到目前为止,我们的应用是无状态的,每次请求处理完成后,数据就会丢失。如果我们需要存储用户注册信息、文章列表、商品数据等,就需要一个持久化的数据存储方案。
这通常意味着使用数据库。数据库可以是关系型数据库(如 PostgreSQL, MySQL, SQLite)或 NoSQL 数据库(如 MongoDB, Redis)。
为什么需要数据库?
- 持久性: 数据在应用关闭后仍然存在。
- 结构化: 可以按照预定义的结构(表、集合)组织数据。
- 可查询性: 可以高效地查询、过滤和排序大量数据。
- 并发处理: 可以处理多个用户同时访问和修改数据。
- 数据完整性: 可以定义规则来保证数据的准确性和一致性。
Flask 如何与数据库集成?
Flask 本身并不内置数据库功能。这符合其微框架的设计理念——将选择权交给开发者。您可以根据项目需求选择任何数据库和相应的 Python 库来与之交互。
最常见的方式是使用 ORM (Object-Relational Mapper) 工具,它们可以将数据库操作转换为 Python 对象和方法的调用,让您可以用 Python 代码来操作数据库,而无需直接编写 SQL 语句。
Flask 社区为许多流行的数据库和 ORM 提供了强大的扩展:
- Flask-SQLAlchemy: SQLAlchemy 是 Python 中最流行的 ORM 之一,支持多种数据库。Flask-SQLAlchemy 是 Flask 对 SQLAlchemy 的封装,使其在 Flask 应用中配置和使用更加方便。
- Flask-Migrate: 用于数据库模式迁移(如创建、修改表结构),通常与 Flask-SQLAlchemy 结合使用。
- Flask-MongoEngine / Flask-PyMongo: 用于与 MongoDB 交互。
- 等等。
对于初学者来说,与数据库集成是 Flask 开发中相对复杂的一步,因为它涉及到数据库本身的概念以及 ORM 的使用。
概念示例 (非 runnable 代码):
假设您使用 Flask-SQLAlchemy,通常流程会是这样:
- 安装 Flask-SQLAlchemy:
pip install Flask-SQLAlchemy
-
在
app.py
中配置数据库 URI 并创建SQLAlchemy
实例:
“`python
from flask import Flask
from flask_sqlalchemy import SQLAlchemyapp = Flask(name)
配置数据库 URI,例如使用 SQLite 数据库文件
app.config[‘SQLALCHEMY_DATABASE_URI’] = ‘sqlite:///site.db’
关闭 SQLALCHEMY_TRACK_MODIFICATIONS,可以节省资源并避免警告
app.config[‘SQLALCHEMY_TRACK_MODIFICATIONS’] = False
db = SQLAlchemy(app) # 创建 SQLAlchemy 实例
3. 定义数据模型(对应数据库中的表):
python定义一个 User 模型,对应 users 表
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)def __repr__(self): return f'<User {self.username}>'
4. 在应用上下文中创建数据库表(首次运行或模型更改时):
python
if name == ‘main‘:
with app.app_context(): # 在应用上下文中执行
db.create_all() # 创建所有定义的表
app.run(debug=True)
5. 在视图函数中进行数据库操作:
python
@app.route(‘/users’)
def list_users():
users = User.query.all() # 查询所有用户
return render_template(‘users.html’, users=users) # 渲染模板显示用户列表@app.route(‘/add_user’, methods=[‘POST’])
def add_user():
username = request.form.get(‘username’)
email = request.form.get(’email’)
new_user = User(username=username, email=email)
db.session.add(new_user) # 添加新用户到会话
db.session.commit() # 提交会话,保存到数据库
return redirect(url_for(‘list_users’))
“`
这只是一个非常基础的示例。实际使用中还会涉及更复杂的查询、数据关系、事务管理等。对于入门阶段,了解数据库的必要性以及 Flask 通过扩展与数据库集成的方式即可。深入学习数据库集成(特别是 Flask-SQLAlchemy)是您掌握 Flask 后续的重要一步。
12. 会话 (Sessions)
HTTP 是无状态协议,这意味着服务器不会记住前一个请求的任何信息。但很多 Web 应用需要记住用户的状态,例如用户是否登录、购物车里有哪些商品等。Flask 提供了会话 (Sessions) 功能来实现这一点。
会话数据存储在服务器端(或加密存储在客户端的 Cookie 中),并且与每个用户的请求相关联。Flask 默认将会话数据加密存储在客户端的 Cookie 中,但这需要一个密钥来保证安全。
使用会话:
-
设置密钥 (
SECRET_KEY
): 这是使用会话的前提,必须设置一个随机且复杂的密钥来加密会话 Cookie。“`python
from flask import Flask, sessionapp = Flask(name)
设置一个密钥,用于加密会话数据。在生产环境中,请使用一个复杂的随机值!
app.config[‘SECRET_KEY’] = ‘your_secret_key_goes_here_replace_with_a_complex_random_string’
… (其他路由) …
@app.route(‘/login’)
def login():
# 模拟用户登录成功
session[‘username’] = ‘authenticated_user’
return ‘You are logged in!’@app.route(‘/profile’)
def profile():
# 检查用户是否在会话中
if ‘username’ in session:
username = session[‘username’]
return f’Welcome, {username}! This is your profile page.’
else:
return ‘You are not logged in. Please Login.’@app.route(‘/logout’)
def logout():
# 从会话中移除用户信息
session.pop(‘username’, None) # 使用 pop() 更安全,如果键不存在不会出错
return ‘You have been logged out.’… (if name == ‘main‘: …)
“`
代码解释:
app.config['SECRET_KEY'] = ...
: 在应用配置中设置SECRET_KEY
。from flask import session
: 导入session
对象。session['username'] = '...'
: 将数据存储到会话中。session
对象表现得像一个字典。if 'username' in session:
: 检查某个键是否存在于会话中。username = session['username']
或username = session.get('username')
: 从会话中获取数据。session.pop('username', None)
: 从会话中删除数据。
安全性注意:
SECRET_KEY
必须保密且复杂: 不要将其硬编码在代码中上传到公共仓库。在生产环境中,应从环境变量或其他安全配置源加载。- 不要在客户端会话中存储敏感数据: 虽然数据是加密的,但客户端仍然拥有 Cookie。敏感数据(如密码、信用卡号)应存储在服务器端(例如数据库),会话中只存储一个标识符(如用户 ID)。
13. 错误处理
当用户访问一个不存在的页面或应用内部发生错误时,显示一个友好的错误页面而不是默认的浏览器错误提示或 Flask 的调试信息(如果关闭了调试模式)是非常重要的。
您可以使用 @app.errorhandler()
装饰器来定制特定 HTTP 状态码的错误页面。
“`python
from flask import Flask, render_template
app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # 记得设置密钥
… (其他路由和配置) …
@app.errorhandler(404)
def page_not_found(error):
“””定制 404 页面.”””
# 注意:错误处理函数必须接收一个 error 参数,通常是 HTTPException 实例
current_year = 2023
# 渲染 404.html 模板,并返回 404 状态码
return render_template(‘404.html’, current_year=current_year), 404
您也可以定制其他错误码,例如 500 内部服务器错误
@app.errorhandler(500)
def internal_server_error(error):
current_year = 2023
return render_template(‘500.html’, current_year=current_year), 500
创建 templates/404.html 文件
“””
{% extends “base.html” %}
{% block title %}Page Not Found{% endblock %}
{% block page_header %}Oops! Page Not Found{% endblock %}
{% block content %}
The page you are looking for does not exist or has been moved.
{% endblock %}
“””
… (if name == ‘main‘: …)
“`
重新运行应用,并尝试访问一个不存在的 URL(例如 http://127.0.0.1:5000/nonexistent_page
)。您应该会看到定制的 404 错误页面。
错误处理函数接收一个 error
参数,其中包含了错误的相关信息。函数需要返回一个响应和一个状态码。
14. 组织大型应用 (蓝图简介)
随着应用的增长,所有的路由和视图函数都集中在一个 app.py
文件中会变得难以管理。对于中大型项目,推荐使用 Flask 的蓝图 (Blueprints) 功能来组织应用。
蓝图是一种组织相关的视图函数、静态文件和模板的方式。它们是应用的一部分,但不是真正的应用实例。您可以在一个单独的文件中创建一个蓝图,定义它的路由等等,然后将这个蓝图注册到主应用实例上。
蓝图的好处:
- 模块化: 将应用的不同部分(如用户认证模块、博客模块、管理后台模块)划分为独立的蓝图。
- 可重用性: 蓝图可以在不同的 Flask 项目中注册和重用。
- URL 前缀和子域名: 可以在注册蓝图时为其指定 URL 前缀或子域名,使得蓝图内的所有路由都自动带有这个前缀。
- 独立的静态文件和模板目录: 每个蓝图都可以有自己的
static
和templates
目录。
概念示例 (非 runnable 代码):
假设您想将用户相关的路由(如登录、注册、个人资料)组织起来:
-
创建蓝图文件: 在项目根目录下创建一个新的文件夹,例如
auth
。在auth
文件夹中创建一个__init__.py
文件来定义蓝图。“`python
auth/init.py
from flask import Blueprint, render_template, request, redirect, url_for
创建一个蓝图实例
第一个参数是蓝图的名称,用于 url_for() 函数
第二个参数是蓝图所在的包/模块名称
auth_bp = Blueprint(‘auth’, name,
template_folder=’templates’, # 指定蓝图自己的模板文件夹
static_folder=’static’, # 指定蓝图自己的静态文件夹
url_prefix=’/auth’) # 为蓝图内的所有路由添加前缀@auth_bp.route(‘/register’, methods=[‘GET’, ‘POST’])
def register():
# 注册逻辑…
if request.method == ‘POST’:
# 处理注册表单
pass # 实际处理代码
return render_template(‘auth/register.html’) # 渲染蓝图内的模板@auth_bp.route(‘/login’, methods=[‘GET’, ‘POST’])
def login():
# 登录逻辑…
if request.method == ‘POST’:
# 处理登录表单
pass # 实际处理代码
return render_template(‘auth/login.html’)@auth_bp.route(‘/logout’)
def logout():
# 登出逻辑…
pass # 实际处理代码
return redirect(url_for(‘index’)) # 重定向到主应用的路由
``
template_folder
注意:和
static_folder` 是相对于蓝图文件所在的目录的路径。 -
在主应用中注册蓝图: 修改
app.py
文件,导入蓝图并注册。“`python
app.py
from flask import Flask, render_template
from auth.init import auth_bp # 导入蓝图实例app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # 记得设置密钥… (其他配置、数据库等) …
注册蓝图到主应用
url_prefix 可以在这里指定,也可以在蓝图定义时指定,通常二选一
app.register_blueprint(auth_bp)
@app.route(‘/’)
def index():
# … (首页逻辑) …
return render_template(‘index.html’, …)… (其他主应用的路由) …
if name == ‘main‘:
# … (创建数据库等) …
app.run(debug=True)
“`
注册蓝图后:
- 访问
/auth/register
会执行auth_bp
蓝图中的register
视图函数。 - 在蓝图内部,使用
url_for('auth.login')
来生成登录页面的 URL (/auth/login
)。auth
是蓝图的名称。 - 在主应用或其他蓝图内部,也可以使用
url_for('auth.register')
来生成/auth/register
。 render_template('auth/register.html')
会查找蓝图指定的模板文件夹中的register.html
文件。
蓝图是构建复杂 Flask 应用的关键工具,它可以帮助您保持代码的整洁和有序。
15. 下一步:部署、测试、更高级功能和常用扩展
通过上面的学习,您已经掌握了 Flask 的核心基础。但要构建一个完整的、可用于生产环境的 Web 应用,还有很多内容需要学习和实践。
- 部署 (Deployment): Flask 的开发服务器 (
app.run()
) 不适合生产环境,它不支持多进程,性能和安全性都不足。您需要使用生产级的 WSGI 服务器来运行 Flask 应用,例如 Gunicorn、uWSGI。然后将 WSGI 服务器部署到 Web 服务器(如 Nginx 或 Apache)后面,由 Web 服务器处理静态文件和反向代理请求。云服务平台(如 Heroku, AWS Elastic Beanstalk, DigitalOcean App Platform, PythonAnywhere)通常也提供方便的部署方式。 - 测试 (Testing): 编写测试用例是保证应用质量的关键。Flask 提供了测试客户端,可以模拟请求,方便进行单元测试和集成测试。Python 的
unittest
或pytest
是常用的测试框架。 - 更高级的 Flask 功能:
- 请求上下文和应用上下文: 理解
request
和g
等全局对象是如何工作的。 - 信号 (Signals): 允许在特定事件发生时(如请求开始、请求结束)通知其他部分的代码。
- 配置管理: 使用配置文件或环境变量来管理应用配置,特别是敏感信息(如数据库凭据、SECRET_KEY)。
- 请求上下文和应用上下文: 理解
- 常用的 Flask 扩展: 社区提供了丰富的扩展来简化常见的 Web 开发任务。学习和使用这些扩展可以极大地提高开发效率:
- Flask-SQLAlchemy: 数据库 ORM 支持。
- Flask-Migrate: 数据库模式迁移。
- Flask-WTF: 集成 WTForms 库,简化表单创建、渲染和验证。
- Flask-Login: 用户会话管理、认证和授权。
- Flask-RESTful / Flask-RESTx: 构建 RESTful API。
- Flask-Mail: 发送邮件。
- Flask-Cache: 添加缓存功能。
- 还有很多其他扩展,可以在 Flask 官方扩展列表 或 PyPI 上查找。
- 安全性: 学习常见的 Web 安全威胁(如 XSS, CSRF, SQL 注入)以及如何在 Flask 应用中防范它们。使用 Flask 扩展(如 Flask-WTF 内置的 CSRF 防护)和遵循安全编码实践至关重要。
学习 Flask 是一个持续的过程。从基础开始,逐步深入到数据库、用户认证、API 开发、部署等更高级的主题。
16. 总结
恭喜您完成了 Flask 入门指南的学习!
在这篇文章中,我们一起:
- 了解了 Flask 的特性和优势。
- 搭建了 Python 开发环境和虚拟环境。
- 安装了 Flask。
- 创建并运行了第一个 Flask 应用。
- 学习了应用实例、路由和视图函数这三个核心概念。
- 掌握了如何处理不同的 URL、HTTP 方法和 URL 变量。
- 学会了使用 Jinja2 模板引擎渲染动态 HTML 页面。
- 了解了如何提供静态文件(CSS、JS、图片)。
- 学习了如何使用
request.form
获取表单提交的数据,并使用redirect
和url_for
进行重定向。 - 初步理解了数据存储(数据库)的重要性以及 Flask 通过扩展集成的模式。
- 学习了如何使用会话存储用户特定信息。
- 了解了如何定制错误处理页面。
- 简要介绍了蓝图,它是组织大型 Flask 应用的关键。
Flask 是一个功能强大且灵活的框架,它鼓励您理解底层的 Web 工作原理,并根据项目需求自由选择组件。通过不断实践和深入学习,您将能够利用 Flask 构建出各种类型的 Web 应用。
最好的学习方式就是动手实践!尝试扩展您的第一个 Flask 应用,添加更多功能,比如:
- 创建一个简单的博客,可以发布和显示文章(需要数据库)。
- 实现用户注册和登录功能(需要用户认证扩展)。
- 构建一个简单的待办事项列表应用(需要表单和数据存储)。
祝您在 Flask 的开发旅程中取得成功!