SqlAlchemy 2.0 中文文档(四)(1)

简介: SqlAlchemy 2.0 中文文档(四)


原文:docs.sqlalchemy.org/en/20/contents.html

进一步阅读

原文:docs.sqlalchemy.org/en/20/tutorial/further_reading.html

下面的章节是讨论本教程中概念的主要顶级章节,更详细地描述了每个子系统的许多其他特性。

核心基础参考

  • 与引擎和连接工作
  • 模式定义语言
  • SQL 语句和表达式 API
  • SQL 数据类型对象

ORM 基础参考

  • ORM 映射类配置
  • 关系配置
  • 使用会话
  • ORM 查询指南

SQLAlchemy ORM

原文:docs.sqlalchemy.org/en/20/orm/index.html

在这里,介绍并完整描述了对象关系映射器。如果你想要使用为你自动构建的更高级别的 SQL,并且自动持久化 Python 对象,请首先转到教程。

  • ORM 快速入门
  • 声明模型
  • 创建一个引擎
  • 发出 CREATE TABLE DDL
  • 创建对象并持久化
  • 简单 SELECT
  • 带 JOIN 的 SELECT
  • 进行更改
  • 一些删除操作
  • 深入学习上述概念
  • ORM 映射类配置
  • ORM 映射类概述
  • 使用声明式映射类
  • 与 dataclasses 和 attrs 的集成
  • 将 SQL 表达式作为映射属性
  • 更改属性行为
  • 复合列类型
  • 映射类继承层次结构
  • 非传统映射
  • 配置版本计数器
  • 类映射 API
  • 关系配置
  • 基本关系模式
  • 邻接列表关系
  • 配置关系连接的方式
  • 处理大型集合
  • 集合定制和 API 详细信息
  • 特殊关系持久化模式
  • 使用传统的 ‘backref’ 关系参数
  • 关系 API
  • ORM 查询指南
  • 为 ORM 映射类编写 SELECT 语句
  • 编写继承映射的 SELECT 语句
  • 启用 ORM 的 INSERT、UPDATE 和 DELETE 语句
  • 列加载选项
  • 关系加载技术
  • 用于查询的 ORM API 功能
  • 遗留查询 API
  • 使用会话
  • 会话基础知识
  • 状态管理
  • 级联操作
  • 事务和连接管理
  • 附加持久化技术
  • 上下文/线程本地会话
  • 使用事件跟踪查询、对象和会话的更改
  • 会话 API
  • 事件和内部原理
  • ORM 事件
  • ORM 内部
  • ORM 异常
  • ORM 扩展
  • 异步 I/O(asyncio)
  • 关联代理
  • 自动映射
  • 烘焙查询
  • 声明式扩展
  • Mypy / Pep-484 支持 ORM 映射
  • 变异跟踪
  • 排序列表
  • 水平分片
  • 混合属性
  • 可索引
  • 替代类仪器
  • ORM 示例
  • 映射示例
  • 继承映射示例
  • 特殊 API
  • 扩展 ORM

ORM 快速入门

原文:docs.sqlalchemy.org/en/20/orm/quickstart.html

对于想要快速了解基本 ORM 使用情况的新用户,这里提供了 SQLAlchemy 统一教程中使用的映射和示例的缩写形式。这里的代码可以从干净的命令行完全运行。

由于本节中的描述故意非常简短,请继续阅读完整的 SQLAlchemy 统一教程以获得对这里所说明的每个概念更深入的描述。

从 2.0 版本开始更改:ORM 快速入门已更新为最新的PEP 484兼容功能,使用包括mapped_column()在内的新构造。有关迁移信息,请参见 ORM 声明模型部分。

声明模型

在这里,我们定义模块级别的构造,这些构造将形成我们将从数据库查询的结构。这个结构被称为声明式映射,它一次定义了 Python 对象模型,以及描述存在或将存在于特定数据库中的真实 SQL 表的数据库元数据:

>>> from typing import List
>>> from typing import Optional
>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy import String
>>> from sqlalchemy.orm import DeclarativeBase
>>> from sqlalchemy.orm import Mapped
>>> from sqlalchemy.orm import mapped_column
>>> from sqlalchemy.orm import relationship
>>> class Base(DeclarativeBase):
...     pass
>>> class User(Base):
...     __tablename__ = "user_account"
...
...     id: Mapped[int] = mapped_column(primary_key=True)
...     name: Mapped[str] = mapped_column(String(30))
...     fullname: Mapped[Optional[str]]
...
...     addresses: Mapped[List["Address"]] = relationship(
...         back_populates="user", cascade="all, delete-orphan"
...     )
...
...     def __repr__(self) -> str:
...         return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
>>> class Address(Base):
...     __tablename__ = "address"
...
...     id: Mapped[int] = mapped_column(primary_key=True)
...     email_address: Mapped[str]
...     user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
...
...     user: Mapped["User"] = relationship(back_populates="addresses")
...
...     def __repr__(self) -> str:
...         return f"Address(id={self.id!r}, email_address={self.email_address!r})"

映射始于一个基类,这个基类上面称为Base,并且是通过对DeclarativeBase类进行简单子类化来创建的。

然后通过对Base进行子类化来创建单独的映射类。一个映射类通常指的是单个特定的数据库表,其名称通过使用__tablename__类级别属性指示。

接下来,通过添加包含称为Mapped的特殊类型注释的属性来声明表的一部分列。每个属性的名称对应于要成为数据库表的一部分的列。每个列的数据类型首先从与每个Mapped注释相关联的 Python 数据类型中获取;int用于INTEGERstr用于VARCHAR,等等。空值性取决于是否使用了Optional[]类型修饰符。可以使用右侧mapped_column()指令中的 SQLAlchemy 类型对象指示更具体的类型信息,例如上面在User.name列中使用的String数据类型。可以使用类型注释映射来自定义 Python 类型和 SQL 类型之间的关联。

mapped_column()指令用于所有需要更具体定制的基于列的属性。除了类型信息外,此指令还接受各种参数,指示有关数据库列的特定详细信息,包括服务器默认值和约束信息,例如在主键和外键中的成员资格。mapped_column()指令接受的参数是 SQLAlchemy Column类所接受的参数的一个超集,该类由 SQLAlchemy 核心用于表示数据库列。

所有 ORM 映射类都要求至少声明一个列作为主键的一部分,通常是通过在那些应该成为主键的mapped_column()对象上使用Column.primary_key参数来实现的。在上面的示例中,User.idAddress.id列被标记为主键。

综合考虑,字符串表名称以及列声明列表的组合在 SQLAlchemy 中被称为 table metadata。在 SQLAlchemy 统一教程的处理数据库元数据中介绍了如何使用核心和 ORM 方法设置表元数据。上述映射是所谓的注释声明表配置的示例。

Mapped 的其他变体可用,最常见的是上面指示的 relationship() 构造。与基于列的属性相比,relationship() 表示两个 ORM 类之间的关联。在上面的示例中,User.addressesUserAddress 连接起来,Address.userAddressUser 连接起来。relationship() 构造介绍于 SQLAlchemy 统一教程 的 处理 ORM 相关对象 部分。

最后,上面的示例类包括一个 __repr__() 方法,这并非必需,但对调试很有用。映射类可以使用诸如 __repr__() 之类的方法自动生成,使用数据类。有关数据类映射的更多信息,请参阅 声明式数据类映射。

创建一个引擎

Engine 是一个工厂,可以为我们创建新的数据库连接,还在 连接池 中保存连接以便快速重用。出于学习目的,我们通常使用一个 SQLite 内存数据库方便起见:

>>> from sqlalchemy import create_engine
>>> engine = create_engine("sqlite://", echo=True)

小贴士

echo=True 参数表示连接发出的 SQL 将被记录到标准输出。

Engine 的完整介绍从 建立连接 - 引擎 开始。

发出 CREATE TABLE DDL

利用我们的表格元数据和引擎,我们可以一次性在目标 SQLite 数据库中生成我们的模式,使用的方法是 MetaData.create_all()

>>> Base.metadata.create_all(engine)
BEGIN  (implicit)
PRAGMA  main.table_...info("user_account")
...
PRAGMA  main.table_...info("address")
...
CREATE  TABLE  user_account  (
  id  INTEGER  NOT  NULL,
  name  VARCHAR(30)  NOT  NULL,
  fullname  VARCHAR,
  PRIMARY  KEY  (id)
)
...
CREATE  TABLE  address  (
  id  INTEGER  NOT  NULL,
  email_address  VARCHAR  NOT  NULL,
  user_id  INTEGER  NOT  NULL,
  PRIMARY  KEY  (id),
  FOREIGN  KEY(user_id)  REFERENCES  user_account  (id)
)
...
COMMIT 

我们刚刚写的那小段 Python 代码发生了很多事情。要完整了解表格元数据的情况,请在教程中继续阅读 处理数据库元数据 部分。

创建对象并持久化

现在我们已经准备好向数据库插入数据了。我们通过创建UserAddress类的实例来实现这一目标,这些类已经通过声明性映射过程自动建立了__init__()方法。然后,我们使用一个名为 Session 的对象将它们传递给数据库,该对象利用Engine与数据库进行交互。这里使用了Session.add_all()方法一次添加多个对象,并且Session.commit()方法将被用来提交数据库中的任何挂起更改,然后提交当前的数据库事务,无论何时使用Session时,该事务始终处于进行中:

>>> from sqlalchemy.orm import Session
>>> with Session(engine) as session:
...     spongebob = User(
...         name="spongebob",
...         fullname="Spongebob Squarepants",
...         addresses=[Address(email_address="spongebob@sqlalchemy.org")],
...     )
...     sandy = User(
...         name="sandy",
...         fullname="Sandy Cheeks",
...         addresses=[
...             Address(email_address="sandy@sqlalchemy.org"),
...             Address(email_address="sandy@squirrelpower.org"),
...         ],
...     )
...     patrick = User(name="patrick", fullname="Patrick Star")
...
...     session.add_all([spongebob, sandy, patrick])
...
...     session.commit()
BEGIN  (implicit)
INSERT  INTO  user_account  (name,  fullname)  VALUES  (?,  ?)  RETURNING  id
[...]  ('spongebob',  'Spongebob Squarepants')
INSERT  INTO  user_account  (name,  fullname)  VALUES  (?,  ?)  RETURNING  id
[...]  ('sandy',  'Sandy Cheeks')
INSERT  INTO  user_account  (name,  fullname)  VALUES  (?,  ?)  RETURNING  id
[...]  ('patrick',  'Patrick Star')
INSERT  INTO  address  (email_address,  user_id)  VALUES  (?,  ?)  RETURNING  id
[...]  ('spongebob@sqlalchemy.org',  1)
INSERT  INTO  address  (email_address,  user_id)  VALUES  (?,  ?)  RETURNING  id
[...]  ('sandy@sqlalchemy.org',  2)
INSERT  INTO  address  (email_address,  user_id)  VALUES  (?,  ?)  RETURNING  id
[...]  ('sandy@squirrelpower.org',  2)
COMMIT 

提示

建议以上述上下文管理器风格使用Session,即使用 Python 的 with: 语句。Session 对象代表了活动的数据库资源,因此确保在完成一系列操作时将其关闭是很好的。在下一节中,我们将保持一个Session仅用于说明目的。

关于创建Session的基础知识请参见使用 ORM Session 执行,更多内容请查看使用 Session 的基础知识。

然后,在使用 ORM 工作单元模式插入行中介绍了一些基本持久性操作的变体。

简单的 SELECT

在数据库中有一些行之后,这是发出 SELECT 语句以加载一些对象的最简单形式。要创建 SELECT 语句,我们使用 select() 函数创建一个新的 Select 对象,然后使用一个 Session 调用它。在查询 ORM 对象时经常有用的方法是 Session.scalars() 方法,它将返回一个 ScalarResult 对象,该对象将遍历我们已选择的 ORM 对象:

>>> from sqlalchemy import select
>>> session = Session(engine)
>>> stmt = select(User).where(User.name.in_(["spongebob", "sandy"]))
>>> for user in session.scalars(stmt):
...     print(user)
BEGIN  (implicit)
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  IN  (?,  ?)
[...]  ('spongebob',  'sandy')
User(id=1, name='spongebob', fullname='Spongebob Squarepants')
User(id=2, name='sandy', fullname='Sandy Cheeks')

上述查询还使用了 Select.where() 方法添加 WHERE 条件,并且还使用了 SQLAlchemy 类似列的构造中的 ColumnOperators.in_() 方法来使用 SQL IN 操作符。

有关如何选择对象和单独列的更多细节请参见选择 ORM 实体和列。

使用 JOIN 进行 SELECT

在一次性查询多个表格是非常常见的,在 SQL 中,JOIN 关键字是这种情况的主要方式。Select 构造使用 Select.join() 方法创建连接:

>>> stmt = (
...     select(Address)
...     .join(Address.user)
...     .where(User.name == "sandy")
...     .where(Address.email_address == "sandy@sqlalchemy.org")
... )
>>> sandy_address = session.scalars(stmt).one()
SELECT  address.id,  address.email_address,  address.user_id
FROM  address  JOIN  user_account  ON  user_account.id  =  address.user_id
WHERE  user_account.name  =  ?  AND  address.email_address  =  ?
[...]  ('sandy',  'sandy@sqlalchemy.org')
>>> sandy_address
Address(id=2, email_address='sandy@sqlalchemy.org')

上述查询演示了多个 WHERE 条件的使用,这些条件会自动使用 AND 进行链接,以及如何使用 SQLAlchemy 类似列对象创建“相等性”比较,这使用了重写的 Python 方法 ColumnOperators.__eq__() 来生成 SQL 条件对象。

有关上述概念的更多背景信息在 WHERE 子句和明确的 FROM 子句和 JOIN 处。

进行更改

Session对象与我们的 ORM 映射类UserAddress结合使用,自动跟踪对对象的更改,这些更改将在下次Session flush 时生成 SQL 语句。 在下面,我们更改了与“sandy”关联的一个电子邮件地址,并在发出 SELECT 以检索“patrick”的行后向“patrick”添加了一个新的电子邮件地址:

>>> stmt = select(User).where(User.name == "patrick")
>>> patrick = session.scalars(stmt).one()
SELECT  user_account.id,  user_account.name,  user_account.fullname
FROM  user_account
WHERE  user_account.name  =  ?
[...]  ('patrick',)
>>> patrick.addresses.append(Address(email_address="patrickstar@sqlalchemy.org"))
SELECT  address.id  AS  address_id,  address.email_address  AS  address_email_address,  address.user_id  AS  address_user_id
FROM  address
WHERE  ?  =  address.user_id
[...]  (3,)
>>> sandy_address.email_address = "sandy_cheeks@sqlalchemy.org"
>>> session.commit()
UPDATE  address  SET  email_address=?  WHERE  address.id  =  ?
[...]  ('sandy_cheeks@sqlalchemy.org',  2)
INSERT  INTO  address  (email_address,  user_id)  VALUES  (?,  ?)
[...]  ('patrickstar@sqlalchemy.org',  3)
COMMIT 

注意当我们访问patrick.addresses时,会发出一个 SELECT。 这称为延迟加载。 关于使用更多或更少 SQL 访问相关项目的不同方式的背景介绍在加载策略中引入。

有关 ORM 数据操作的详细说明始于使用 ORM 进行数据操作。

一些删除

一切都必须有个了结,就像我们的一些数据库行一样 - 这里是两种不同形式的删除的快速演示,这两种删除根据特定用例的不同而重要。

首先,我们将从sandy用户中删除一个Address对象。 当Session下次 flush 时,这将导致该行被删除。 此行为是我们在映射中配置的称为删除级联的东西。 我们可以使用Session.get()按主键获取sandy对象的句柄,然后使用该对象:

>>> sandy = session.get(User, 2)
BEGIN  (implicit)
SELECT  user_account.id  AS  user_account_id,  user_account.name  AS  user_account_name,  user_account.fullname  AS  user_account_fullname
FROM  user_account
WHERE  user_account.id  =  ?
[...]  (2,)
>>> sandy.addresses.remove(sandy_address)
SELECT  address.id  AS  address_id,  address.email_address  AS  address_email_address,  address.user_id  AS  address_user_id
FROM  address
WHERE  ?  =  address.user_id
[...]  (2,) 

上面的最后一个 SELECT 是延迟加载操作进行,以便加载sandy.addresses集合,以便我们可以删除sandy_address成员。有其他方法可以完成这一系列操作,这些方法不会生成太多的 SQL。

我们可以选择发出 DELETE SQL,以删除到目前为止已更改的内容,而不提交事务,使用Session.flush()方法:

>>> session.flush()
DELETE  FROM  address  WHERE  address.id  =  ?
[...]  (2,) 

接下来,我们将完全删除“patrick”用户。 对于对象本身的顶级删除,我们使用Session.delete()方法; 此方法实际上不执行删除,而是设置对象将在下次 flush 时被删除。 该操作还将根据我们配置的级联选项级联到相关对象,本例中为相关的Address对象:

>>> session.delete(patrick)
SELECT  user_account.id  AS  user_account_id,  user_account.name  AS  user_account_name,  user_account.fullname  AS  user_account_fullname
FROM  user_account
WHERE  user_account.id  =  ?
[...]  (3,)
SELECT  address.id  AS  address_id,  address.email_address  AS  address_email_address,  address.user_id  AS  address_user_id
FROM  address
WHERE  ?  =  address.user_id
[...]  (3,) 

在这种特殊情况下,Session.delete()方法发出了两个 SELECT 语句,即使它没有发出 DELETE,这可能看起来令人惊讶。这是因为当该方法去检查对象时,发现patrick对象已经过期,这是在我们上次调用Session.commit()时发生的,发出的 SQL 是为了重新从新事务加载行。这种过期是可选的,并且在正常使用中,我们经常会在不适用的情况下关闭它。

为了说明被删除的行,这里是提交:

>>> session.commit()
DELETE  FROM  address  WHERE  address.id  =  ?
[...]  (4,)
DELETE  FROM  user_account  WHERE  user_account.id  =  ?
[...]  (3,)
COMMIT 

教程讨论了 ORM 删除,详见使用工作单元模式删除 ORM 对象。对象过期的背景信息在过期/刷新;级联在级联中进行了深入讨论。

深入学习上述概念

对于新用户来说,上面的部分可能是一个快速浏览。上面的每一步中都有许多重要的概念没有涵盖到。通过快速了解事物的外观,建议通过 SQLAlchemy 统一教程逐步学习,以获得对上面所发生的事物的坚实的工作知识。祝你好运!

声明模型

在这里,我们定义了将构成我们从数据库查询的模块级构造。这个结构被称为声明性映射,它一次定义了 Python 对象模型以及描述真实 SQL 表的数据库元数据,这些表存在或将存在于特定数据库中:

>>> from typing import List
>>> from typing import Optional
>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy import String
>>> from sqlalchemy.orm import DeclarativeBase
>>> from sqlalchemy.orm import Mapped
>>> from sqlalchemy.orm import mapped_column
>>> from sqlalchemy.orm import relationship
>>> class Base(DeclarativeBase):
...     pass
>>> class User(Base):
...     __tablename__ = "user_account"
...
...     id: Mapped[int] = mapped_column(primary_key=True)
...     name: Mapped[str] = mapped_column(String(30))
...     fullname: Mapped[Optional[str]]
...
...     addresses: Mapped[List["Address"]] = relationship(
...         back_populates="user", cascade="all, delete-orphan"
...     )
...
...     def __repr__(self) -> str:
...         return f"User(id={self.id!r}, name={self.name!r}, fullname={self.fullname!r})"
>>> class Address(Base):
...     __tablename__ = "address"
...
...     id: Mapped[int] = mapped_column(primary_key=True)
...     email_address: Mapped[str]
...     user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
...
...     user: Mapped["User"] = relationship(back_populates="addresses")
...
...     def __repr__(self) -> str:
...         return f"Address(id={self.id!r}, email_address={self.email_address!r})"

映射始于一个基类,上面称为Base,通过对DeclarativeBase类进行简单的子类化来创建。

通过对Base进行子类化,然后创建个体映射类。一个映射类通常指的是一个特定的数据库表,其名称是通过使用__tablename__类级属性指示的。

接下来,声明表中的列,通过添加包含一个特殊的类型注释称为Mapped的属性来实现。每个属性的名称对应于要成为数据库表的列。每个列的数据类型首先取自与每个Mapped注释相关联的 Python 数据类型;对于 INTEGER 使用 int,对于 VARCHAR 使用 str 等。可选性取决于是否使用了 Optional[] 类型修饰符。可以使用右侧的 SQLAlchemy 类型对象指示更具体的类型信息,例如上面在 User.name 列中使用的 String 数据类型。Python 类型和 SQL 类型之间的关联可以使用 type annotation map 进行定制。

mapped_column() 指令用于所有需要更具体定制的基于列的属性。除了类型信息外,该指令还接受各种参数,指示有关数据库列的特定细节,包括服务器默认值和约束信息,例如主键和外键的成员资格。mapped_column() 指令接受了 SQLAlchemy Column 类接受的参数的超集,该类由 SQLAlchemy Core 用于表示数据库列。

所有的 ORM 映射类都需要至少声明一个列作为主键的一部分,通常是通过在应该成为键的那些mapped_column()对象上使用Column.primary_key参数来实现的。在上面的示例中,User.idAddress.id 列被标记为主键。

综合起来,SQLAlchemy 中一个字符串表名和列声明列表的组合被称为 table metadata。在 SQLAlchemy  统一教程中介绍了使用 Core 和 ORM 方法设置表元数据的方法,在 Working with Database Metadata  章节中。上述映射是 Annotated Declarative Table 配置的示例。

还有其他Mapped的变体可用,最常见的是上面指示的relationship()构造。与基于列的属性相反,relationship()表示两个 ORM 类之间的链接。在上面的示例中,User.addressesUser链接到AddressAddress.userAddress链接到Userrelationship()构造在 SQLAlchemy 统一教程中的使用 ORM 相关对象中进行介绍。

最后,上面的示例类包括一个 __repr__() 方法,虽然不是必需的,但对于调试很有用。映射类可以使用诸如 __repr__() 这样的方法自动生成,使用数据类。有关数据类映射的更多信息,请参阅声明性数据类映射。


SqlAlchemy 2.0 中文文档(四)(2)https://developer.aliyun.com/article/1562995

相关文章
|
3月前
|
测试技术 API 数据库
SqlAlchemy 2.0 中文文档(九)(5)
SqlAlchemy 2.0 中文文档(九)
21 0
|
3月前
|
SQL 前端开发 数据库
SqlAlchemy 2.0 中文文档(六)(1)
SqlAlchemy 2.0 中文文档(六)
36 0
|
3月前
|
SQL 测试技术 API
SqlAlchemy 2.0 中文文档(一)(1)
SqlAlchemy 2.0 中文文档(一)
104 1
SqlAlchemy 2.0 中文文档(一)(1)
|
3月前
|
存储 SQL API
SqlAlchemy 2.0 中文文档(四)(5)
SqlAlchemy 2.0 中文文档(四)
28 3
|
3月前
|
SQL 存储 数据库
SqlAlchemy 2.0 中文文档(一)(3)
SqlAlchemy 2.0 中文文档(一)
46 1
|
3月前
|
SQL API 数据库
SqlAlchemy 2.0 中文文档(四)(2)
SqlAlchemy 2.0 中文文档(四)
30 1
|
3月前
|
SQL 存储 API
SqlAlchemy 2.0 中文文档(十)(5)
SqlAlchemy 2.0 中文文档(十)
21 1
|
3月前
|
SQL 测试技术 Python
SqlAlchemy 2.0 中文文档(二)(1)
SqlAlchemy 2.0 中文文档(二)
34 2
|
3月前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(二)(4)
SqlAlchemy 2.0 中文文档(二)
31 2
|
3月前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(二)(2)
SqlAlchemy 2.0 中文文档(二)
40 2