第63课 - Flask 与数据库集成(SQLAlchemy)
学习目标
- 理解 ORM(对象关系映射)的基本概念及其在 Web 开发中的优势。
- 掌握在 Flask 应用中配置和初始化 SQLAlchemy 的方法。
- 学会使用 SQLAlchemy 定义数据模型(表结构)。
- 熟练运用 SQLAlchemy 进行基本的数据库增删改查(CRUD)操作。
- 能够将数据库操作集成到 Flask 路由中,构建动态数据驱动的 Web 应用。
核心概念
1. 什么是 ORM?
想象一下,你正在用 Python 写代码,而数据库只懂 SQL 语言。你需要一个"翻译官"来帮你把 Python 的"命令"(对象、属性、方法)翻译成数据库能听懂的 SQL 语句,并把数据库返回的结果再翻译回 Python 对象。这个"翻译官"就是 ORM (Object-Relational Mapping,对象关系映射)。
使用 ORM 的好处是:
- 更 Pythonic:用操作对象的方式操作数据库,代码更易读、易写。
- 数据库无关性:理论上,更换数据库(如从 SQLite 换到 PostgreSQL)只需修改配置,而无需重写所有数据库查询代码。
- 提高开发效率:避免了手动编写和拼接复杂的 SQL 语句。
2. SQLAlchemy 与 Flask-SQLAlchemy
SQLAlchemy 是 Python 生态中最强大、最流行的 ORM 工具之一。Flask-SQLAlchemy 则是它的一个轻量级扩展,专门为 Flask 应用做了适配,简化了集成和配置过程。
3. 核心组件
- 应用 (App) 和配置 (Config):Flask 应用实例,包含数据库连接字符串(
SQLALCHEMY_DATABASE_URI)等配置。 - 引擎 (Engine):SQLAlchemy 核心,负责管理数据库连接池和实际通信。
- 会话 (Session):所有数据库操作的"沙箱"。我们通过会话来添加、修改、删除对象,并最终通过
commit()提交事务使更改生效。 - 模型 (Model):一个普通的 Python 类,继承自
db.Model。这个类映射到数据库中的一张表。类属性定义了表的列。
代码示例
以下是一个完整的示例,演示如何用 Flask 和 SQLAlchemy 创建一个简单的"用户管理"应用。
步骤 1:安装依赖
pip install flask flask-sqlalchemy
步骤 2:创建应用 (app.py)
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
# 1. 初始化 Flask 应用
app = Flask(__name__)
# 2. 配置数据库 (这里使用SQLite,文件会自动创建)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'your-secret-key' # 用于闪存消息
# 3. 初始化数据库实例
db = SQLAlchemy(app)
# 4. 定义数据模型 (User表)
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}>'
# 5. 在第一次请求前创建数据库表
@app.before_request
def create_tables():
db.create_all()
# 6. 路由:首页,显示所有用户
@app.route('/')
def index():
users = User.query.all() # 查询所有用户
return render_template('index.html', users=users)
# 7. 路由:添加用户
@app.route('/add', methods=['POST'])
def add_user():
username = request.form.get('username')
email = request.form.get('email')
if username and email:
new_user = User(username=username, email=email)
db.session.add(new_user)
db.session.commit()
flash('用户添加成功!', 'success')
else:
flash('用户名和邮箱不能为空!', 'danger')
return redirect(url_for('index'))
# 8. 路由:删除用户
@app.route('/delete/<int:user_id>')
def delete_user(user_id):
user_to_delete = User.query.get_or_404(user_id) # 根据ID查询,不存在则返回404
db.session.delete(user_to_delete)
db.session.commit()
flash('用户已删除!', 'success')
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
步骤 3:创建模板 (templates/index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理</title>
</head>
<body>
<h1>用户列表</h1>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<p class="{{ category }}">{{ message }}</p>
{% endfor %}
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('add_user') }}">
<input type="text" name="username" placeholder="用户名" required>
<input type="email" name="email" placeholder="邮箱" required>
<button type="submit">添加</button>
</form>
<ul>
{% for user in users %}
<li>
{{ user.username }} ({{ user.email }})
<a href="{{ url_for('delete_user', user_id=user.id) }}">删除</a>
</li>
{% else %}
<li>暂无用户。</li>
{% endfor %}
</ul>
</body>
</html>
运行与演示:
- 运行
python app.py。 - 访问
http://127.0.0.1:5000/,你将看到一个简单的用户管理界面。 - 尝试添加和删除用户。数据将被持久化存储在当前目录下的
instance/site.db文件中。
实践练习
练习 1:基础扩展
在现有代码的基础上,为 User 模型添加一个 age (整数) 字段。修改 add_user 路由和模板以支持年龄输入,并确保在显示用户列表时也能看到年龄。
练习 2:实现编辑功能
创建一个新的路由 /edit/<int:user_id>,该路由:
- 对于 GET 请求,显示一个预填充了用户当前信息的表单。
- 对于 POST 请求,接收表单数据并更新数据库中的对应用户。
在
index.html的每个用户条目旁添加一个"编辑"链接。
练习 3:引入关系查询(进阶)
创建一个新的模型 Post,表示用户发布的文章。Post 模型应包含 id, title, content, user_id (外键) 等字段。在 User 模型中添加与 Post 的一对多关系。修改用户详情页面,使其能显示该用户发布的所有文章标题。
常见错误
-
忘记在应用上下文中运行代码:
- 错误:在应用初始化完成前(或在独立的 Python 脚本中)直接运行
db.create_all()。 - 解决:使用
app.before_request钩子或在if __name__ == '__main__':块内,先with app.app_context(): db.create_all()。
- 错误:在应用初始化完成前(或在独立的 Python 脚本中)直接运行
-
模型定义后未重新创建表:
- 错误:修改了模型(如添加新字段),但数据库中已存在的表结构没有更新,导致
OperationalError。 - 解决:在开发阶段,最简单的办法是删除旧的数据库文件(如
site.db),然后重启应用让db.create_all()重新创建。生产环境应使用 数据库迁移工具(如 Flask-Migrate)。
- 错误:修改了模型(如添加新字段),但数据库中已存在的表结构没有更新,导致
-
混淆
query.get()与query.filter():- 错误:使用
User.query.get(username)试图按用户名查询(get只适用于主键查询)。 - 解决:按非主键字段查询应使用
User.query.filter_by(username=username).first()。
- 错误:使用
-
忘记提交会话:
- 错误:调用了
db.session.add()或db.session.delete(),但没有调用db.session.commit()。 - 结果:对数据库的更改不会生效,内存中的对象状态已变,但数据库无变化。
- 错误:调用了
小结
本课我们学习了如何将强大的 SQLAlchemy ORM 集成到 Flask 应用中。核心要点包括:
- ORM 是连接 Python 对象和关系数据库的桥梁,让数据操作更直观。
- Flask-SQLAlchemy 提供了便捷的接口来配置数据库和定义模型。
- 数据模型 通过继承
db.Model类并定义类属性来映射到数据库表。 - 数据库操作 通过
db.session对象进行,遵循添加/修改/删除 -> 提交的事务模式。 - CRUD 操作 可以轻松地与 Flask 路由结合,构建完整的数据驱动应用。
掌握了 Flask 与 SQLAlchemy 的集成,你就拥有了构建大多数 Web 应用所需的核心数据持久化能力。下一课,我们将学习如何使用 Flask 构建 REST API,将这些数据以标准化的方式提供给前端或第三方客户端。