1. Flask 简介
1.1 什么是 Flask
Flask 是一个用 Python 编写的轻量级 Web 框架,被誉为 微框架。它提供基础功能,如路由、请求处理和模板引擎,但不强迫开发者使用特定库或工具,赋予开发人员高度的自由选择权,以满足不同项目的需求。
简单来说,Flask 帮助 Python 开发者快速构建 Web 应用,提供以下核心功能:
- 路由: 将 URL 与对应的 Python 函数进行匹配,实现 URL 到函数的映射。
- 请求处理: 处理来自用户的 HTTP 请求,例如获取请求参数、解析请求数据等,并将处理结果返回给用户。
- 模板引擎: 使用 Jinja2 模板引擎动态渲染网页,将数据嵌入到 HTML 模板中,生成最终的 HTML 页面。
1.2 Flask 的优势
- 轻量级和灵活: Flask 没有强迫性要求,允许开发者自由选择数据库、模板引擎和其他库,完全掌控项目的架构和组件选择。
- 易于学习: Flask 的语法简洁直观,易于理解和掌握,即使是初学者也能快速上手。
- 强大的社区支持: Flask 拥有庞大而活跃的社区,开发者可以轻松获取各种资源、教程、示例代码和帮助,解决开发中的问题。
- 可扩展性: Flask 提供扩展机制,允许开发者通过添加扩展来扩展功能,例如数据库集成、用户认证、表单验证、邮件发送等,满足不同项目的需求。
1.3 Flask 的应用场景
Flask 适合以下应用场景:
- 小型项目: 对于简单的网站或 API,Flask 提供快速开发和部署方案,节省时间和成本。
- 原型开发: Flask 帮助开发者快速构建原型,验证想法的可行性,并进行快速迭代和修改。
- 个性化需求: Flask 的灵活性满足定制化的 API、特定领域应用等需求,可以根据项目的特殊要求进行灵活调整和扩展。
一些使用 Flask 的知名应用:
- Pinterest: 一家图片社交网站,使用 Flask 构建了其核心功能。
- Lyft: 一家打车服务公司,使用 Flask 构建了其 API 和 Web 应用。
- Twilio: 一家云通信平台,使用 Flask 构建了其 API 和管理界面。
1.4 Flask 的基本架构
Flask 的基本架构包括以下几个关键部分:
- WSGI 应用: Flask 应用本身是一个 WSGI 应用,负责接收 HTTP 请求并生成响应。
- 路由: 使用
@app.route
装饰器将 URL 与 Python 函数进行绑定,实现 URL 到函数的映射。 - 请求处理: Flask 提供
request
对象,用于获取 HTTP 请求信息,例如请求方法、请求参数、请求头等。 - 模板引擎: Flask 默认使用 Jinja2 模板引擎,用于渲染动态网页。
- 响应生成: Flask 使用
render_template
函数渲染模板,或使用make_response
函数创建响应对象,并返回给用户。
了解这些基本架构有助于理解 Flask 的工作原理,以及如何使用 Flask 构建 Web 应用。
2. 快速上手
2.1 安装 Flask
使用 pip 安装 Flask:
pip install Flask
2.2 创建第一个 Flask 应用
创建名为 app.py
的文件,并添加以下代码:
from flask import Flaskapp = Flask(__name__)@app.route('/')
def index():return 'Hello, Flask!'if __name__ == '__main__':app.run(debug=True)
代码解释:
from flask import Flask
: 导入 Flask 库。app = Flask(__name__)
: 创建一个 Flask 应用实例,__name__
表示当前模块名,用于指定应用的根目录。@app.route('/')
: 定义一个路由,将根路径/
与index
函数关联。def index():
: 定义一个函数,返回一个字符串 “Hello, Flask!”。if __name__ == '__main__':
: 确保代码只在直接运行脚本时执行,而不是被其他模块导入时执行。app.run(debug=True)
: 运行 Flask 应用,开启调试模式。
2.3 运行 Flask 应用
在终端中运行以下命令:
python app.py
打开浏览器,访问 http://127.0.0.1:5000/
,你将看到 “Hello, Flask!” 的输出。
2.4 使用路由
路由用于将 URL 与 Python 函数进行匹配。在上面的代码中,@app.route('/')
定义了一个路由,将根路径 /
与 index
函数关联。
你可以定义多个路由,例如:
@app.route('/about')
def about():return 'This is the about page.'@app.route('/contact')
def contact():return 'This is the contact page.'
访问 http://127.0.0.1:5000/about
和 http://127.0.0.1:5000/contact
将分别显示 “This is the about page.” 和 “This is the contact page.”。
2.5 处理 HTTP 请求方法
Flask 可以区分不同的 HTTP 请求方法,例如 GET
、POST
、PUT
、DELETE
等。
可以使用 methods
参数来指定允许的 HTTP 请求方法:
@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == 'GET':return 'Login page'elif request.method == 'POST':# 处理 POST 请求return 'Login successful'
代码解释:
methods=['GET', 'POST']
: 允许使用GET
和POST
请求方法访问/login
路由。request.method
: 获取当前请求方法。
访问 http://127.0.0.1:5000/login
将显示 “Login page”,使用 POST 请求则会显示 “Login successful”。
3. 模板渲染
模板渲染是将数据动态插入到 HTML 页面中,生成最终的 HTML 内容,Flask 使用 Jinja2 模板引擎来实现模板渲染。
3.1 Jinja2 模板引擎
Jinja2 是一个功能强大的模板引擎,它提供了一套语法来定义模板,并支持变量、循环、条件语句等功能。
3.2 使用模板渲染页面
-
创建模板文件
在应用的根目录下创建
templates
目录,并将模板文件放在该目录下。例如,创建一个名为index.html
的模板文件:<!DOCTYPE html> <html> <head><title>My Flask App</title> </head> <body><h1>Hello, {{ name }}!</h1> </body> </html>
-
使用
render_template()
函数渲染模板在视图函数中使用
render_template()
函数渲染模板,并将数据传递到模板中。from flask import Flask, render_templateapp = Flask(__name__)@app.route('/') def index():name = 'World'return render_template('index.html', name=name)if __name__ == '__main__':app.run(debug=True)
这段代码将
index.html
模板渲染,并传递name
变量的值到模板中,最终生成的 HTML 内容为:<!DOCTYPE html> <html> <head><title>My Flask App</title> </head> <body><h1>Hello, World!</h1> </body> </html>
3.3 传递数据到模板
在 render_template()
函数中,可以使用关键字参数将数据传递到模板中。
传递单个变量:
return render_template('index.html', name='World')
传递多个变量:
return render_template('index.html', name='World', age=25)
传递列表:
names = ['Alice', 'Bob', 'Charlie']
return render_template('index.html', names=names)
传递字典:
user = {'name': 'Alice', 'age': 25}return render_template('index.html', user=user)
3.4 模板继承
Jinja2 支持模板继承,允许你定义一个基础模板,并在其他模板中继承该基础模板。
基础模板 (base.html
):
<!DOCTYPE html>
<html>
<head><title>{% block title %}My Flask App{% endblock %}</title>
</head>
<body><header><h1>Welcome to My Flask App</h1></header><main>{% block content %}{% endblock %}</main><footer><p>© 2023 My Flask App</p></footer>
</body>
</html>
继承模板 (index.html
):
{% extends 'base.html' %}{% block title %}Home{% endblock %}{% block content %}
<h2>Hello, {{ name }}!</h2>
{% endblock %}
在继承模板中,可以使用 {% block %}
标签定义可覆盖的区域,并使用 {% extends %}
标签继承基础模板。
3.5 模板中的控制语句
Jinja2 支持在模板中使用控制语句,例如循环和条件语句。
循环语句:
<ul>{% for name in names %}<li>{{ name }}</li>{% endfor %}
</ul>
条件语句:
{% if user.is_authenticated %}<p>Welcome, {{ user.name }}!</p>
{% else %}<p>Please log in.</p>
{% endif %}
4. 数据库集成
Flask 通过集成 SQLAlchemy 等数据库 ORM 库,实现与数据库的交互。
4.1 安装 SQLAlchemy
使用 pip 安装 Flask-SQLAlchemy 扩展:
pip install Flask-SQLAlchemy
4.2 配置数据库
在应用的配置中添加数据库连接字符串,例如使用 SQLite 数据库:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
4.3 定义模型
使用 SQLAlchemy 定义数据库模型,例如用户模型:
from flask_sqlalchemy import SQLAlchemydb = SQLAlchemy(app)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}>'
代码解释:
db = SQLAlchemy(app)
: 创建一个 SQLAlchemy 实例,并绑定到 Flask 应用。class User(db.Model)
: 定义一个用户模型,继承自db.Model
。id = db.Column(db.Integer, primary_key=True)
: 定义id
列,作为主键。username = db.Column(db.String(80), unique=True, nullable=False)
: 定义username
列,字符串类型,必须唯一且非空。email = db.Column(db.String(120), unique=True, nullable=False)
: 定义email
列,字符串类型,必须唯一且非空。
4.4 创建数据库
在交互式 Python 解释器中运行以下命令,创建数据库和表:
>>> from app import db
>>> db.create_all()
4.5 操作数据库
插入数据:
user = User(username='Alice', email='alice@example.com')
db.session.add(user)
db.session.commit()
查询数据:
users = User.query.all()
print(users)
更新数据:
user = User.query.filter_by(username='Alice').first()
user.email = 'alice@newdomain.com'
db.session.commit()
删除数据:
user = User.query.filter_by(username='Alice').first()
db.session.delete(user)
db.session.commit()
4.6 处理数据库关系
SQLAlchemy 支持处理一对多、多对多等复杂关系,例如定义一对多关系:
父模型:
class Author(db.Model):id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(50), nullable=False)books = db.relationship('Book', backref='author', lazy=True)
子模型:
class Book(db.Model):id = db.Column(db.Integer, primary_key=True)title = db.Column(db.String(100), nullable=False)author_id = db.Column(db.Integer, db.ForeignKey('author.id'), nullable=False)
代码解释:
db.relationship('Book', backref='author', lazy=True)
: 在Author
模型中定义一个关系属性books
,表示与Book
模型的一对多关系。db.ForeignKey('author.id')
: 在Book
模型中定义外键author_id
,指向Author
模型的id
列。
5. 表单处理
Flask-WTF 是一个集成 WTForms 的 Flask 扩展,用于处理表单创建、验证和错误处理。
5.1 安装 Flask-WTF
使用 pip 安装 Flask-WTF 扩展:
pip install Flask-WTF
5.2 配置 Flask-WTF
在应用的配置中添加 CSRF 保护的密钥:
app.config['SECRET_KEY'] = 'your_secret_key'
5.3 定义表单
使用 Flask-WTF 定义一个登录表单:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequiredclass LoginForm(FlaskForm):username = StringField('Username', validators=[DataRequired()])password = PasswordField('Password', validators=[DataRequired()])submit = SubmitField('Login')
代码解释:
FlaskForm
: 表单基类。StringField
: 文本输入框。PasswordField
: 密码输入框。SubmitField
: 提交按钮。DataRequired
: 验证器,确保字段不为空。
5.4 渲染表单
在视图函数中创建表单实例,并在模板中渲染表单。
视图函数:
@app.route('/login', methods=['GET', 'POST'])
def login():form = LoginForm()if form.validate_on_submit():# 处理登录逻辑return 'Login successful'return render_template('login.html', form=form)
模板文件 (login.html
):
<!DOCTYPE html>
<html>
<head><title>Login</title>
</head>
<body><form method="POST">{{ form.hidden_tag() }}<p>{{ form.username.label }}<br>{{ form.username(size=32) }}<br>{% for error in form.username.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p><p>{{ form.password.label }}<br>{{ form.password(size=32) }}<br>{% for error in form.password.errors %}<span style="color: red;">[{{ error }}]</span>{% endfor %}</p><p>{{ form.submit() }}</p></form>
</body>
</html>
代码解释:
form.validate_on_submit()
: 检查表单是否通过验证。{{ form.hidden_tag() }}
: 渲染 CSRF 保护的隐藏标签。{{ form.username.label }}
: 渲染表单字段的标签。{{ form.username(size=32) }}
: 渲染表单字段的输入框。{% for error in form.username.errors %}
: 渲染验证错误信息。
5.5 表单验证
在表单定义中添加验证器,例如 Email
验证器:
from wtforms.validators import Emailclass LoginForm(FlaskForm):username = StringField('Username', validators=[DataRequired(), Email()])password = PasswordField('Password', validators=[DataRequired()])submit = SubmitField('Login')
代码解释:
Email()
: 验证器,确保字段是有效的电子邮件地址。
5.6 自定义验证器
可以定义自定义验证器,并在表单定义中使用:
from wtforms.validators import ValidationErrordef custom_validator(form, field):if field.data != 'expected_value':raise ValidationError('Invalid value')class LoginForm(FlaskForm):username = StringField('Username', validators=[DataRequired(), custom_validator])password = PasswordField('Password', validators=[DataRequired()])submit = SubmitField('Login')
代码解释:
custom_validator(form, field)
: 自定义验证器函数,检查字段值是否为预期值。raise ValidationError('Invalid value')
: 抛出验证错误。
6. 用户认证
Flask-Login 是一个用户会话管理扩展,用于处理用户登录、注销和会话管理。
6.1 安装 Flask-Login
使用 pip 安装 Flask-Login 扩展:
pip install Flask-Login
6.2 配置 Flask-Login
在应用中初始化 Flask-Login,并定义用户加载函数:
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_userlogin_manager = LoginManager(app)
login_manager.login_view = 'login'@login_manager.user_loader
def load_user(user_id):return User.query.get(int(user_id))
代码解释:
LoginManager(app)
: 创建一个 LoginManager 实例,并绑定到 Flask 应用。login_manager.login_view = 'login'
: 设置登录视图的端点。@login_manager.user_loader
: 定义用户加载函数,根据用户 ID 加载用户对象。
6.3 定义用户模型
用户模型需要继承 UserMixin
,并实现用户加载函数:
class User(UserMixin, 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)password = db.Column(db.String(128), nullable=False)def __repr__(self):return f'<User {self.username}>'
代码解释:
UserMixin
: 提供用户会话管理所需的方法和属性。
6.4 用户登录
在视图函数中处理用户登录:
@app.route('/login', methods=['GET', 'POST'])
def login():form = LoginForm()if form.validate_on_submit():user = User.query.filter_by(username=form.username.data).first()if user and user.password == form.password.data:login_user(user)return 'Login successful'return render_template('login.html', form=form)
代码解释:
User.query.filter_by(username=form.username.data).first()
: 根据用户名查询用户。login_user(user)
: 登录用户,创建用户会话。
6.5 保护视图
使用 @login_required
装饰器保护需要登录访问的视图:
@app.route('/dashboard')
@login_required
def dashboard():return f'Welcome, {current_user.username}!'
代码解释:
@login_required
: 确保只有登录用户才能访问该视图。current_user
: 获取当前登录的用户对象。
6.6 用户注销
在视图函数中处理用户注销:
@app.route('/logout')
@login_required
def logout():logout_user()return 'You have been logged out.'
代码解释:
logout_user()
: 注销用户,结束用户会话。