SqlAlchemy 2.0 中文文档(四)(1)https://developer.aliyun.com/article/1562994
创建引擎
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 代码发生了很多事情。要完整了解表元数据的情况,请参阅使用数据库元数据中的教程。
创建对象并持久化
我们现在可以将数据插入到数据库中了。我们通过创建User
和Address
类的实例来实现这一点,这些类已经通过声明映射过程自动创建了__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
提示
建议像上面那样使用 Python 的 with:
语句,即使用上下文管理器样式使用Session
。 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 映射类 User
和 Address
一起,会自动跟踪对象的更改,这些更改会导致 SQL 语句在下次 Session
刷新时被发出。下面,我们更改了与“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
下次刷新时,这将导致该行被删除。这种行为是我们在映射中配置的,称为级联删除。我们可以使用 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()
方法;这个方法实际上并不执行删除操作,而是设置对象在下一次刷新时将被删除。该操作还会根据我们配置的级联选项级联到相关对象,本例中是关联的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 对象。关于对象过期的背景信息请参考过期/刷新;级联操作在 Cascades 中有详细讨论。
深入学习上述概念
对于新用户来说,上述部分可能是一场令人眼花缭乱的旅程。每个步骤中都有许多重要概念没有涵盖。快速了解事物的外观后,建议通过 SQLAlchemy 统一教程来深入了解上述内容。祝好运!
ORM 映射类配置
ORM 配置的详细参考,不包括关系,关系详细说明在关系配置。
要快速查看典型的 ORM 配置,请从 ORM 快速入门开始。
要了解 SQLAlchemy 实现的对象关系映射概念,请先查看 SQLAlchemy 统一教程,在使用 ORM 声明形式定义表元数据中介绍。
- ORM 映射类概述
- ORM 映射风格
- 声明性映射
- 命令式映射
- 映射类基本组件
- 待映射的类
- 表或其他来自子句对象
- 属性字典
- 其他映射器配置参数
- 映射类行为
- 默认构造函数
- 跨加载保持非映射状态
- 映射类、实例和映射器的运行时内省
- 映射器对象的检查
- 映射实例的检查
- 使用声明性映射类
- 声明性映射风格
- 使用声明性基类
- 使用装饰器的声明性映射(无声明性基类)
- 使用声明性配置表
- 带有
mapped_column()
的声明性表
- 使用带注释的声明性表(
mapped_column()
的类型注释形式) - 访问表和元数据
- 声明性表配置
- 使用声明性表的显式模式名称
- 为声明式映射的列设置加载和持久化选项
- 显式命名声明式映射列
- 将额外列添加到现有的声明式映射类
- 使用命令式表进行声明式(即混合声明式)
- 映射表列的替代属性名
- 为命令式表列应用加载、持久化和映射选项
- 使用反射表进行声明式映射
- 使用延迟反射
- 使用自动映射
- 从反射表自动化列命名方案
- 映射到显式主键列集合
- 映射表列的子集
- 声明式映射器配置
- 使用声明式定义映射属性
- 声明式的映射器配置选项
- 动态构建映射器参数
- 其他声明式映射指令
__declare_last__()
__declare_first__()
metadata
__abstract__
__table_cls__
- 使用混合组合映射层次结构
- 增强基类
- 混合使用列
- 混合使用关系
- 在
_orm.column_property()
和其他_orm.MapperProperty
类中混合使用 - 使用混合和基类进行映射继承模式
- 使用
_orm.declared_attr()
与继承Table
和Mapper
参数 - 使用
_orm.declared_attr()
生成表特定的继承列
- 从多个混合类组合表/映射器参数
- 使用命名约定在混合类上创建索引和约束
- 与 dataclasses 和 attrs 集成
- 声明式数据类映射
- 类级别功能配置
- 属性配置
- 列默认值
- 与 Annotated 集成
- 使用混合类和抽象超类
- 关系配置
- 使用未映射的数据类字段
- 与 Pydantic 等替代数据类提供者集成
- 将 ORM 映射应用于现有的数据类(传统数据类使用)
- 使用声明式与命令式表映射映射预先存在的数据类
- 使用声明式样式字段映射预先存在的数据类
- 使用预先存在的数据类的声明式混合类
- 使用命令式映射映射预先存在的数据类
- 将 ORM 映射应用于现有的 attrs 类
- 使用声明式“命令式表”映射映射属性
- 使用命令式映射映射属性
- SQL 表达式作为映射属性
- 使用混合类
- 使用 column_property
- 将 column_property() 添加到现有的声明式映射类
- 在映射时从列属性组合
- 使用
column_property()
进行列推迟
- 使用普通描述符
- 查询时将 SQL 表达式作为映射属性
- 更改属性行为
- 简单验证器
validates()
- 在核心级别使用自定义数据类型
- 使用描述符和混合物
- 同义词
synonym()
- 操作符定制
- 复合列类型
- 使用映射的复合列类型
- 复合体的其他映射形式
- 直接映射列,然后传递给复合体
- 直接映射列,将属性名称传递给复合体
- 命令映射和命令表
- 使用传统非数据类
- 跟踪复合体上的原位变化
- 重新定义复合体的比较操作
- 嵌套复合体
- 复合体 API
composite()
- 映射类继承层次结构
- 联接表继承
- 与联接继承相关的关系
- 加载联接继承映射
- 单表继承
- 使用
use_existing_column
解决列冲突 - 与单表继承相关的关系
- 使用
polymorphic_abstract
构建更深层次的层次结构 - 加载单表继承映射
- 具体表继承
- 具体多态加载配置
- 抽象具体类
- 经典和半经典具体多态配置
- 具体继承关系的关系
- 加载具体继承映射
- 非传统映射
- 将类映射到多个表
- 将类映射到任意子查询
- 一个类的多个映射器
- 配置版本计数器
- 简单版本计数
- 自定义版本计数器/类型
- 服务器端版本计数器
- 编程或条件版本计数器
- 类映射 API
registry
registry.__init__()
registry.as_declarative_base()
registry.configure()
registry.dispose()
registry.generate_base()
registry.map_declaratively()
registry.map_imperatively()
registry.mapped()
registry.mapped_as_dataclass()
registry.mappers
registry.update_type_annotation_map()
add_mapped_attribute()
column_property()
declarative_base()
declarative_mixin()
as_declarative()
mapped_column()
declared_attr
declared_attr.cascading
declared_attr.directive
DeclarativeBase
DeclarativeBase.__mapper__
DeclarativeBase.__mapper_args__
DeclarativeBase.__table__
DeclarativeBase.__table_args__
DeclarativeBase.__tablename__
DeclarativeBase.metadata
DeclarativeBase.registry
DeclarativeBaseNoMeta
DeclarativeBaseNoMeta.__mapper__
DeclarativeBaseNoMeta.__mapper_args__
DeclarativeBaseNoMeta.__table__
DeclarativeBaseNoMeta.__table_args__
DeclarativeBaseNoMeta.__tablename__
DeclarativeBaseNoMeta.metadata
DeclarativeBaseNoMeta.registry
has_inherited_table()
synonym_for()
object_mapper()
class_mapper()
configure_mappers()
clear_mappers()
identity_key()
polymorphic_union()
orm_insert_sentinel()
reconstructor()
Mapper
Mapper.__init__()
Mapper.add_properties()
Mapper.add_property()
Mapper.all_orm_descriptors
Mapper.attrs
Mapper.base_mapper
Mapper.c
Mapper.cascade_iterator()
Mapper.class_
Mapper.class_manager
Mapper.column_attrs
Mapper.columns
Mapper.common_parent()
Mapper.composites
Mapper.concrete
Mapper.configured
Mapper.entity
Mapper.get_property()
Mapper.get_property_by_column()
Mapper.identity_key_from_instance()
Mapper.identity_key_from_primary_key()
Mapper.identity_key_from_row()
Mapper.inherits
Mapper.is_mapper
Mapper.is_sibling()
Mapper.isa()
Mapper.iterate_properties
Mapper.local_table
Mapper.mapped_table
Mapper.mapper
Mapper.non_primary
Mapper.persist_selectable
Mapper.polymorphic_identity
Mapper.polymorphic_iterator()
Mapper.polymorphic_map
Mapper.polymorphic_on
Mapper.primary_key
Mapper.primary_key_from_instance()
Mapper.primary_mapper()
Mapper.relationships
Mapper.selectable
Mapper.self_and_descendants
Mapper.single
Mapper.synonyms
Mapper.tables
Mapper.validators
Mapper.with_polymorphic_mappers
MappedAsDataclass
MappedClassProtocol
SqlAlchemy 2.0 中文文档(四)(3)https://developer.aliyun.com/article/1562996