Flask-SQLAlchemy
Flask-SQLAlchemy 是 flask 的一个拓展插件,专门添加对 SQLAlchemy 的支持(ORM,关系对象模型)。使用它可以在 flask 中使用对象直接与 SQLAlchemy 进行交互,大大简化了 SQLAlchemy 与 flask 结合使用的过程,提供了非常方便的一些操作对象,例如引擎、模型、会话、请求等。
Flask-SQLAlchemy 不会改变 SQLAlchemy 的工作或使用方式。请参阅 SQLAlchemy 文档以了解如何深入使用 ORM。此处的文档仅涵盖设置扩展,而不涉及如何使用 SQLAlchemy。
注意,sqlalchemy 2.0 之后与之前的 1.4 差距蛮大。flask-sqlalchemy 3.0 版本与之前的差距也蛮大。使用是请注意版本。
快速开始
Flask-SQLAlchemy 内部封装了很多 SQLAlchemy 的操作来简化 SQLAlchemy 的使用。虽然新增了一些新的有用的功能,但它仍然可以像 SQLAlchemy 一样使用。
本页将带你了解 Flask-SQLAlchemy 的基本使用,有关完整功能和自定义,请参阅文档的其他部分,包括 SQLAlchemy
对象的 API 文档。
查阅 SQLAlchemy 文档
Flask-SQLAlchemy 是 SQLAlchemy 的一些操作。你应该遵循 SQLAlchemy 的教程来了解并使用它,请查询其文档获取有关功能的详细信息。本文展示了如何设置 Flask-SQLAlchemy 本身,而不是如何使用 SQLAlchemy 。Flask-SQLAlchemy 会自动设置引擎、声明模型类、作用域会话,因此你可以跳过 SQLAlchemy 教程的这部分内容。
安装
Flask-SQLAlchemy 在 pypi 上就有,可以直接使用 pip 指令进行安装。例如,要使用最新版本的,可以用 pip 安装或更新到最新版本。
pip install -U Flask-SQLAlchemy
配置插件
唯一需要的 Flask 应用程序配置是 SQLALCHEMY_DATABASE_URI
键。这是一个连接字符串,它告诉 SQLAlchemy 要连接到哪个数据库。
创建 Flask 应用程序对象之后,加载配置,然后通过调用 db.init_app
使用应用程序初始化 SQLAlchemy
扩展类。此示例连接到 SQLite 数据库,该数据库存储在应用程序的实例文件夹中。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# 创建拓展插件实例
db = SQLAlchemy()
# 创建应用程序 app
app = Flask(__name__)
# 配置 SQLite 数据库, 默认存放在 app instance 文件夹下
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
# 讲拓展插件对象绑定到程序实例
db.init_app(app)
db
对象使您可以访问 db.Model
类来定义模型,并访问 db.session
来执行查询。
有关链接字符串的说明以及使用的其他配置键,请参阅 配置 , SQLAlchemy
对象还采用一些参数来自定义它管理的对象。
定义模型
子类 db.Model
以定义模型类。 db
对象使 sqlalchemy
和 sqlalchemy.orm
中的名称方便使用,例如 db.Column
。该模型将通过将 CamelCase
类名转换为 snake_case
来生成表名。
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String)
表名 "user"
将自动分配给模型的表。
有关定义和创建模型和表的更多信息,请参阅模型和表。
创建表
定义所有模型和表后,调用 SQLAlchemy.create_all()
在数据库中创建表模式。这需要一个应用程序上下文。由于此时您不在请求中,请手动创建一个。
with app.app_context():
db.create_all()
如果在其他模块中定义了模型,则必须在调用 create_all
之前导入它们,否则 SQLAlchemy 将不知道它们,就不会创建对应的数据表。
数据查询
在 Flask 视图或 CLI 命令中,可以使用 db.session
来执行查询和修改模型数据。
SQLAlchemy 自动为每个模型定义一个 __init__
方法,该方法将任何关键字参数分配给相应的数据库列和其他属性。
db.session.add(obj)
将对象添加到会话中,以便插入。修改对象的属性会更新对象。 db.session.delete(obj)
删除一个对象。请记住在修改、添加或删除任何数据后调用 db.session.commit()
。
db.session.execute(db.select(...))
构造一个查询以从数据库中选择数据。构建查询是 SQLAlchemy 的主要功能,因此你需要阅读其关于 select 的教程以了解所有相关信息。你通常会使用 Result.scalars()
方法获取结果列表,或使用 Result.scalar()
方法获取单个结果。
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String)
def json(self):
return {"id": self.id, "username": self.username, "email": self.email}
with app.app_context():
db.create_all()
@app.get("/user")
def user_list():
users = db.session.execute(db.select(User).order_by(User.username)).scalars()
return {"message": "ok", "data": [user.json() for user in users]}
@app.post("/user")
def create_user():
data = request.get_json()
user = User(username=data.get("username"), email=data.get("email"))
db.session.add(user)
db.session.commit()
return {"message": "ok", "data": user.json()}
@app.get("/user/<int:uid>")
def user_detail(uid):
user = db.get_or_404(User, uid)
return {"message": "ok", "data": user.json()}
@app.delete("/user/<int:uid>")
def user_delete(uid):
user = db.get_or_404(User, uid)
db.session.delete(user)
db.session.commit()
return {"message": "ok", "data": user.json()}
您可能会看到使用 Model.query
来构建查询。这是一个较旧的查询接口,在 SQLAlchemy 中被认为是遗留的。更喜欢使用 db.session.execute(db.select(...))
。
有关查询的更多信息,请参阅修改和查询数据。
# 测试代码
from faker import Faker
import requests
faker = Faker('zh-CN')
response = requests.get('http://127.0.0.1:5000/user')
print(response.json())
for i in range(10):
user = {
'username': faker.name(),
'email': faker.email(),
}
response = requests.post('http://127.0.0.1:5000/user', json=user)
print(response.json())
response = requests.get('http://127.0.0.1:5000/user/1')
print(response.json())
response = requests.delete('http://127.0.0.1:5000/user/1')
print(response.json())
记住什么
大多数情况下,您应该照常使用 SQLAlchemy。 SQLAlchemy
扩展实例创建、配置并授予对以下内容的访问权限:
SQLAlchemy.Model
声明模型基类。它自动设置表名而不需要__tablename__
。SQLAlchemy.session
是一个作用域为当前 Flask 应用程序上下文的会话。每次请求后都会清理它。SQLAlchemy.metadata
和SQLAlchemy.metadatas
允许访问配置中定义的每个元数据。SQLAlchemy.engine
和SQLAlchemy.engines
允许访问配置中定义的每个引擎。SQLAlchemy.create_all()
创建所有表。- 您必须处于活动的 Flask 应用程序上下文中才能执行查询并访问会话和引擎。
模型与数据表
使用 db.Model
类定义模型,或使用 db.Table
类创建表。两者都处理 Flask-SQLAlchemy 的绑定键以与特定引擎关联。
定义模型
有关以声明方式定义模型类的完整信息,请参阅 SQLAlchemy 的 声明性文档。
子类 db.Model
以创建模型类。这是一个 SQLAlchemy 声明性基类,它将采用 Column
属性并创建一个表。与普通 SQLAlchemy 不同,如果未设置 __tablename__
并且定义了主键列,Flask-SQLAlchemy 的模型将自动生成表名。
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import sqlalchemy as sa
db = SQLAlchemy()
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
db.init_app(app)
class User(db.Model):
id = sa.Column(sa.Integer, primary_key=True)
type = sa.Column(sa.String)
为方便起见,扩展对象提供对 sqlalchemy
和 sqlalchemy.orm
模块中名称的访问。所以你可以使用 db.Column
而不是导入和使用 sqlalchemy.Column
,尽管两者是等价的。
定义模型不会在数据库中创建它。在定义模型和表后使用 create_all()
创建它们。如果您在子模块中定义模型,则必须导入它们,以便 SQLAlchemy 在调用 create_all
之前知道它们。
with app.app_context():
db.create_all()
定义表
有关定义表对象的完整信息,请参阅 SQLAlchemy 的表文档。
创建 db.Table
的实例来定义表。该类采用表名,然后是任何列和其他表部分,例如列和约束。与普通的 SQLAlchemy 不同, metadata
参数不是必需的。将根据 bind_key
参数选择元数据,否则将使用默认值。
直接创建表的一个常见原因是在定义多对多关系时。关联表不需要自己的模型类,因为它将通过相关模型上的相关关系属性进行访问。
import sqlalchemy as sa
user_book_m2m = db.Table(
"user_book",
sa.Column("user_id", sa.ForeignKey(User.id), primary_key=True),
sa.Column("book_id", sa.ForeignKey(Book.id), primary_key=True),
)
修改与查询
增、改、删
有关使用 ORM 修改数据的更多信息,请参阅 SQLAlchemy 的 ORM 教程 和其他 SQLAlchemy 文档。
要插入数据,请将模型对象传递给 db.session.add()
:
user = User()
db.session.add(user)
db.session.commit()
要更新数据,请修改模型对象的属性:
user.verified = True
db.session.commit()
要删除数据,将模型对象传递给 db.session.delete()
:
db.session.delete(user)
db.session.commit()
修改数据后,必须调用 db.session.commit()
将修改提交到数据库。否则,它们将在请求结束时被丢弃。
查询
有关使用 ORM 查询数据的更多信息,请参阅 SQLAlchemy 的查询指南和其他 SQLAlchemy 文档。
查询通过 db.session.execute()
执行。它们可以使用 select()
构造。执行 select 会返回一个 Result
对象,该对象具有许多用于处理返回行的方法。
users = db.session.execute(db.select(User).order_by(User.username)).scalars()
print([user.json() for user in users])
username = '刘桂花'
user = db.session.execute(db.select(User).filter_by(username=username)).scalar()
print(user)
视图查询
如果您编写一个 Flask 视图函数,则返回一个 404 Not Found
错误以丢失条目通常很有用。 Flask-SQLAlchemy 提供了一些额外的查询方法。
SQLAlchemy.get_or_404()
如果具有给定 id 的行不存在,SQLAlchemy.get_or_404()
将引发 404,否则它将返回实例。SQLAlchemy.first_or_404()
如果查询未返回任何结果,SQLAlchemy.first_or_404()
将引发 404,否则将返回第一个结果。SQLAlchemy.one_or_404()
如果查询没有准确返回一个结果,SQLAlchemy.one_or_404()
将引发 404,否则将返回结果。
遗留查询接口
您可能会看到使用 Model.query
或 session.query
来构建查询。该查询接口在 SQLAlchemy 中被认为是遗留的。更喜欢使用 session.execute(select(...))
。 有关文档,请参阅旧版查询接口。
分页查询
如果有很多结果,你可能只想一次显示特定数量,允许用户单击下一个和上一个链接来查看数据页面。这有时称为分页,并使用 paginate
实现。
在 select 语句上调用 SQLAlchemy.paginate()
以获得 Pagination
对象。
在请求期间,这将从查询字符串 request.args
中获取 page
和 per_page
参数。传递 max_per_page
以防止用户在单个页面上请求太多结果。如果未给出,默认值为第 1 页,每页 20 个项目。
page = db.paginate(db.select(User).order_by(User.id))
print(page.items)