学习 Flask:你的第一个 Web 应用
欢迎来到激动人心的 Web 开发世界!如果你是一位 Python 爱好者,并且渴望将你的代码带到互联网上,那么 Flask 是一个绝佳的起点。作为一个轻量级的 Web 服务框架,Flask 以其简洁、灵活的特性赢得了众多开发者的喜爱。它不强制使用任何特定的数据库或模板引擎,这给了你极大的自由度来选择最适合你的工具。
在这篇文章中,我们将一起踏上学习 Flask 的旅程,从零开始构建你的第一个 Web 应用。我们将涵盖环境搭建、核心概念、模板使用、静态文件处理等基础知识,让你快速入门并亲手运行一个简单的 Web 站点。
准备好了吗?让我们开始吧!
1. 准备工作:搭建你的开发环境
在开始编写代码之前,我们需要确保你的开发环境已经准备就绪。
1.1 安装 Python
首先,确保你的系统上已经安装了 Python。推荐使用 Python 3.6 或更高版本。你可以在 Python 官方网站 下载并安装适合你操作系统的版本。安装完成后,打开终端或命令行工具,输入以下命令检查 Python 版本:
“`bash
python –version
或者
python3 –version
“`
如果你看到输出了版本号,说明 Python 已经安装成功。
1.2 创建并激活虚拟环境
在 Python 项目中,强烈推荐使用虚拟环境(Virtual Environment)。虚拟环境可以隔离不同项目所需的库版本,避免相互冲突。
在你的项目目录下(例如,创建一个名为 my-flask-app
的文件夹),打开终端,执行以下命令创建虚拟环境:
“`bash
在 my-flask-app 文件夹内执行
python -m venv venv
或者
python3 -m venv venv
“`
这会在当前目录下创建一个名为 venv
的文件夹,其中包含了独立的 Python 环境。
接下来,你需要激活这个虚拟环境。激活方法取决于你的操作系统:
- macOS / Linux:
bash
source venv/bin/activate - Windows (Command Prompt):
bash
venv\Scripts\activate.bat - Windows (PowerShell):
powershell
venv\Scripts\Activate.ps1
激活后,你的终端提示符前会显示 (venv)
,表示你当前正处于这个虚拟环境中。现在,你在这个环境中安装的所有库都只会存在于 venv
文件夹内,不会污染全局 Python 环境。
1.3 安装 Flask
虚拟环境激活后,我们就可以使用 Python 的包管理工具 pip
来安装 Flask 了。
bash
pip install Flask
等待安装完成。你可以输入 pip list
查看当前环境中安装的库,确认 Flask 是否安装成功。
bash
pip list
你应该能在列表中看到 Flask 和它的一些依赖库,如 Jinja2 (用于模板)、Werkzeug (一个强大的 WSGI 工具集,Flask 建立其上) 等。
至此,你的开发环境已经搭建完毕。
2. 你的第一个 Flask 应用:”Hello, World!”
现在,让我们来编写 Flask 的经典入门程序——“Hello, World!”。
在你的项目目录下(即 my-flask-app
文件夹),创建一个名为 app.py
的文件。
“`python
app.py
from flask import Flask
创建一个 Flask 应用实例
name 参数是 Python 模块的内置变量,用于指示模块的名称。
Flask 使用这个参数来查找应用所在的根目录,
从而确定静态文件和模板文件的位置。
app = Flask(name)
使用装饰器 @app.route() 定义 URL 路由。
当用户访问应用的根 URL (‘/’) 时,会触发下面的 index 函数。
@app.route(‘/’)
def index():
# 函数返回的内容将作为 HTTP 响应发送给用户的浏览器。
return ‘Hello, World!’
这个条件判断确保只有在直接运行此脚本时(而不是作为模块导入时)
才会执行 app.run()。
if name == ‘main‘:
# app.run() 启动 Flask 的内置开发服务器。
# debug=True 开启调试模式。在调试模式下,当你的代码发生修改时,
# 服务器会自动重新加载;如果发生错误,浏览器会显示一个有用的调试页面。
app.run(debug=True)
“`
保存这个文件。
2.1 运行你的应用
回到你的终端,确保虚拟环境仍然激活,然后运行这个 Python 文件:
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 开发服务器已经启动,并且正在监听 http://127.0.0.1:5000/
这个地址和端口。127.0.0.1
是本地回环地址,5000
是 Flask 默认使用的端口号。
现在,打开你的 Web 浏览器,访问 http://127.0.0.1:5000/
。
你应该能在浏览器中看到白底黑字的 “Hello, World!” 文本。
恭喜!你已经成功运行了你的第一个 Flask Web 应用!
你可以尝试修改 index()
函数的返回字符串,保存文件,然后刷新浏览器。由于开启了 debug=True
,服务器会自动重新加载,你将看到修改后的内容。
要停止服务器,回到终端,按 Ctrl + C
。
2.2 代码解析
让我们回顾一下 app.py
的每一部分:
from flask import Flask
: 导入 Flask 类,它是所有 Flask 应用的基础。app = Flask(__name__)
: 创建一个 Flask 应用的实例。__name__
帮助 Flask 确定项目的根目录。@app.route('/')
: 这是一个装饰器(decorator)。它将紧随其后的函数注册为一个视图函数,并将其关联到指定的 URL 路径。这里的'/'
表示应用的根 URL。def index():
: 定义了一个 Python 函数。当用户访问'/'
路径时,Flask 会调用这个函数。return 'Hello, World!'
: 视图函数必须返回一个响应。最简单的响应就是一个字符串,Flask 会将其作为 HTTP 响应体发送给浏览器。除了字符串,还可以返回 HTML、模板渲染结果、重定向等。if __name__ == '__main__':
: 这是 Python 的标准用法,确保app.run()
只在直接运行app.py
文件时执行。app.run(debug=True)
: 启动 Flask 开发服务器。debug=True
开启调试模式,非常方便开发。
3. 核心概念:路由与视图函数
在 Flask 中,Web 应用的核心功能在于将特定的 URL 映射到执行相应逻辑的 Python 函数上。这个映射过程就是路由 (Routing),而处理特定请求的函数就是视图函数 (View Function)。
我们已经在“Hello, World!”示例中看到了最简单的路由和视图函数:@app.route('/')
将根路径映射到 index()
函数。
3.1 添加更多路由
一个 Web 应用通常不止一个页面。让我们为应用添加一个“关于我们”页面。
修改 app.py
,添加另一个视图函数和路由:
“`python
app.py (添加了关于我们页面)
from flask import Flask
app = Flask(name)
@app.route(‘/’)
def index():
return ‘Hello, World!’
新增的路由和视图函数
@app.route(‘/about’)
def about():
return ‘This is the About page.’
if name == ‘main‘:
app.run(debug=True)
“`
运行 python app.py
,然后访问 http://127.0.0.1:5000/
和 http://127.0.0.1:5000/about
。你应该能在不同的 URL 上看到不同的内容。
3.2 动态路由
有时候,URL 中包含了一些可变的信息,比如用户 ID 或文章 slug。Flask 允许你使用动态路由来捕获这些信息。
在 URL 规则中使用尖括号 <variable_name>
来定义一个动态部分。这个动态部分会作为参数传递给视图函数。
例如,创建一个用户个人资料页面:
“`python
app.py (添加了动态路由)
from flask import Flask
app = Flask(name)
@app.route(‘/’)
def index():
return ‘Hello, World!’
@app.route(‘/about’)
def about():
return ‘This is the About page.’
动态路由示例
@app.route(‘/user/
def show_user_profile(username):
# username 会从 URL 中捕获并传递给函数
return f’User: {username}’
带有类型转换的动态路由
表示 post_id 必须是一个整数
@app.route(‘/post/
def show_post(post_id):
# post_id 会被自动转换为整数
return f’Post ID: {post_id}’
if name == ‘main‘:
app.run(debug=True)
“`
运行应用,尝试访问 http://1.27.0.0.1:5000/user/alice
和 http://127.0.0.1:5000/user/bob
。你会看到不同的用户名显示。
再尝试访问 http://127.0.0.1:5000/post/123
。你会看到 Post ID。如果你尝试访问 http://127.0.0.1:5000/post/abc
,Flask 会返回一个 404 Not Found 错误,因为 'abc'
不能转换为整数。
常用的类型转换器包括:
* string
(默认): 接受任何不包含斜杠的字符串。
* int
: 接受正整数。
* float
: 接受正浮点数。
* path
: 接受包含斜杠的字符串。
* uuid
: 接受 UUID 字符串。
4. 使用模板:分离逻辑与呈现
目前为止,我们的视图函数直接返回简单的字符串。但在实际的 Web 应用中,页面通常是复杂的 HTML 结构。将 HTML 代码直接写在 Python 字符串中是非常不方便且难以维护的。
这就是模板引擎的作用。模板引擎允许我们将页面的结构(HTML)与应用逻辑(Python 代码)分离开来。Flask 默认集成了 Jinja2 模板引擎,它功能强大且易于使用。
4.1 创建模板文件夹
Flask 会在你的应用根目录下的一个名为 templates
的子文件夹中查找模板文件。所以,首先在你的 my-flask-app
项目文件夹中创建 templates
文件夹。
my-flask-app/
├── venv/
├── app.py
└── templates/
└── index.html <-- 我们将要创建的模板文件
4.2 编写第一个模板
在 templates
文件夹中创建一个名为 index.html
的文件,写入以下内容:
“`html
Welcome to My Flask App!
Hello, {{ name }}!
This is the homepage rendered from a template.
“`
这个 HTML 文件看起来很标准,但请注意 <p>Hello, {{ name }}!</p>
中的 {{ name }}
。这是 Jinja2 模板引擎的语法,用于显示一个变量的值。{{ ... }}
结构表示一个变量或表达式。
4.3 在视图函数中渲染模板
现在,修改 app.py
中的 index()
视图函数,让它不再返回字符串,而是渲染我们刚刚创建的 index.html
模板。我们需要从 flask
模块中导入 render_template
函数。
“`python
app.py (使用模板)
from flask import Flask, render_template # 导入 render_template
app = Flask(name)
@app.route(‘/’)
def index():
# 使用 render_template 函数来渲染模板。
# 第一个参数是模板文件的名称(相对于 templates 文件夹)。
# 后续的关键字参数会将变量传递给模板。
return render_template(‘index.html’, name=’World’) # 传递变量 name
@app.route(‘/about’)
def about():
# 你也可以为其他页面创建模板
return render_template(‘about.html’) # 假设你创建了 about.html
… 其他路由保持不变 …
@app.route(‘/user/
def show_user_profile(username):
return render_template(‘user.html’, username=username) # 传递动态变量
if name == ‘main‘:
app.run(debug=True)
“`
可选: 如果你为 /about
和 /user/<username>
也想使用模板,你需要在 templates
文件夹中分别创建 about.html
和 user.html
文件。
例如,user.html
可以这样写:
“`html
User Profile
Username: {{ username }}
“`
运行 python app.py
,访问 http://127.0.0.1:5000/
。你将看到一个更漂亮的页面,其中 “Hello, World!” 现在是 “Hello, World!”,因为我们在 render_template
中将 name
变量的值设为了 'World'
。
尝试修改 return render_template('index.html', name='Alice')
,保存并刷新,你会看到页面变成了 “Hello, Alice!”。
4.4 Jinja2 模板基础语法
Jinja2 模板除了变量输出 {{ ... }}
,还支持一些基本的逻辑和控制结构:
- 表达式和变量:
{{ variable }}
,{{ user.name }}
,{{ list[0] }}
,{{ "Hello" + name }}
等。 - 控制流: 使用
{% ... %}
结构。- If/Else 语句:
html
{% if user %}
<p>Hello, {{ user.name }}!</p>
{% else %}
<p>Hello, Guest!</p>
{% endif %} - For 循环:
html
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
- If/Else 语句:
- 注释:
{# 这是注释 #}
- 宏 (Macros): 类似函数的模板代码块,可以重复使用。
- 模板继承: 允许你定义一个基础布局(父模板),然后子模板可以继承父模板并在特定区域(blocks)覆盖或添加内容。这对于构建一致的站点结构非常有用。
虽然在你的第一个应用中可能不会立即用到所有这些高级特性,但了解它们的存在对于构建更复杂的页面至关重要。模板继承是 Jinja2 最强大的特性之一,强烈推荐在后续学习中深入了解。
5. 处理静态文件:CSS, JavaScript 和图片
一个现代的 Web 应用通常包含 CSS 样式表、JavaScript 脚本和图片等文件。这些文件被称为静态文件 (Static Files),因为它们通常是固定不变的,服务器直接将其发送给浏览器,不需要动态生成。
Flask 约定在应用根目录下的一个名为 static
的子文件夹中查找静态文件。
5.1 创建静态文件夹
在你的项目目录下创建 static
文件夹。你可以在其中再创建子文件夹,例如 css
、js
、images
等,以更好地组织文件。
my-flask-app/
├── venv/
├── app.py
├── templates/
│ └── index.html
└── static/
└── css/
└── style.css <-- 我们将要创建的静态文件
5.2 编写静态文件
在 static/css
文件夹中创建一个名为 style.css
的文件,添加一些简单的 CSS 规则:
“`css
/ static/css/style.css /
body {
margin: 20px;
background-color: #f4f4f4;
color: #333;
font-family: Arial, sans-serif;
}
h1 {
color: #0056b3;
}
p {
line-height: 1.6;
}
“`
5.3 在模板中引用静态文件
要在 HTML 模板中引用静态文件,我们需要使用 Flask 提供的 url_for()
函数。url_for()
函数可以根据视图函数的名称生成 URL,它也可以用来生成静态文件的 URL。
在模板中使用 url_for()
生成静态文件 URL 的语法是 {{ url_for('static', filename='...') }}
。filename
参数是相对于 static
文件夹的路径。
修改 templates/index.html
文件,在 <head>
标签中添加对 style.css
的引用:
“`html
Welcome to My Flask App!
Hello, {{ name }}!
This is the homepage rendered from a template.
“`
运行 python app.py
,访问 http://127.0.0.1:5000/
。你会看到页面应用了 style.css
中的样式(背景色、字体等)。
url_for()
函数的好处在于,即使你的 URL 路由发生变化,只要静态文件的路径不变,它生成的 URL 依然是正确的,避免了硬编码 URL 带来的维护问题。
6. 处理表单数据: GET 和 POST
Web 应用的一个常见任务是收集用户的输入,例如通过 HTML 表单。用户提交表单时,数据会被发送到服务器,服务器需要接收和处理这些数据。最常见的 HTTP 方法是 GET 和 POST。
- GET: 数据以 URL 参数的形式发送,适合获取数据或无副作用的操作。数据量有限且不适合传输敏感信息。
- POST: 数据放在 HTTP 请求体中发送,适合提交数据或有副作用的操作(如创建资源)。数据量理论上无限制且相对更安全(虽然 HTTPS 才是保证传输安全的根本)。
让我们创建一个简单的表单来收集用户的名字,并在另一个页面显示一个问候语。
6.1 创建表单页面模板
在 templates
文件夹中创建一个 greeting_form.html
文件:
“`html
Enter Your Name
``
POST
这个表单使用方法将数据发送到
{{ url_for(‘greet’) }}生成的 URL。输入字段的
name=”username”` 属性是关键,它定义了数据发送到服务器时的键名。
6.2 创建处理表单的视图函数
我们需要一个视图函数来渲染表单页面,以及另一个视图函数来处理表单提交的数据。
在 app.py
中添加以下代码:
“`python
app.py (处理表单)
from flask import Flask, render_template, request, redirect, url_for # 导入 request, redirect, url_for
app = Flask(name)
… 其他路由保持不变 …
用于显示表单的路由,接受 GET 方法
@app.route(‘/form’)
def show_form():
return render_template(‘greeting_form.html’)
用于处理表单提交的路由,接受 POST 方法
注意 methods 参数指定了这个路由只响应 POST 请求
@app.route(‘/greet’, methods=[‘POST’])
def greet():
# 检查请求方法是否为 POST (虽然路由已经限定了,这里是一种额外的检查)
if request.method == ‘POST’:
# request.form 是一个字典状对象,包含了 POST 请求中表单的数据。
# 通过输入字段的 name 属性来获取对应的值。
username = request.form[‘username’]
# 为了防止 Key Error,也可以使用 request.form.get(‘username’),
# 如果键不存在则返回 None。
# username = request.form.get(‘username’)
# 现在,我们有了用户的名字,可以渲染一个结果页面或者重定向。
# 这里我们渲染一个结果页面
return render_template('greeting_result.html', name=username)
# 如果不是 POST 请求(理论上不会发生因为路由限定了方法),
# 可以重定向回表单页面或其他页面。
return redirect(url_for('show_form')) # 导入 url_for 和 redirect
… 确保 app.run(debug=True) 仍在底部 …
if name == ‘main‘:
app.run(debug=True)
“`
6.3 创建结果页面模板
在 templates
文件夹中创建一个 greeting_result.html
文件:
“`html
Greeting
{% if name %}
Hello, {{ name }}!
{% else %}
Hello there!
{% endif %}
“`
注意这里使用了 Jinja2 的 {% if name %}
语法,以防 name
变量没有传递过来。
6.4 测试表单
运行 python app.py
。
访问 http://127.0.0.1:5000/form
。你会看到一个包含输入框和按钮的表单。
在输入框中输入你的名字,然后点击 “Submit”。
你应该会被重定向到 http://127.0.0.1:5000/greet
,页面会显示 “Hello, [你的名字]!”。
6.5 处理 GET 表单数据
如果你将表单的 method
改为 GET
,并且将 /greet
路由的 methods
也改为 ['GET']
(或者不指定,默认就是 GET),那么你需要使用 request.args
而不是 request.form
来获取数据:
“`python
示例:处理 GET 表单数据 (不建议用于敏感信息)
from flask import Flask, render_template, request
app = Flask(name)
@app.route(‘/greet_get’, methods=[‘GET’])
def greet_get():
# request.args 包含 GET 请求的 URL 参数
username = request.args.get(‘username’, ‘Guest’) # 使用 .get() 方法,如果参数不存在则返回默认值 ‘Guest’
return f’Hello, {username} (from GET request)!’