SqlAlchemy 2.0 中文文档(五十七)(6)https://developer.aliyun.com/article/1563168
优化的 ORM 批量插入现在已经针对除 MySQL 之外的所有后端实现了
1.4 系列中引入的显著性能改进,如 ORM Batch inserts with psycopg2 now batch statements with RETURNING in most cases 中所述,现已普遍适用于所有支持 RETURNING 的后端,除了 MySQL:SQLite、MariaDB、PostgreSQL(所有驱动程序)和 Oracle;SQL Server 有支持,但在版本 2.0.9 中暂时禁用 [1]。虽然原始功能对于 psycopg2 驱动程序至关重要,否则在使用 cursor.executemany()
时存在严重的性能问题,但对于其他 PostgreSQL 驱动程序,如 asyncpg,此更改也至关重要,因为在使用 RETURNING 时,单语句 INSERT 语句仍然不可接受地缓慢,以及在使用 SQL Server 时,无论是否使用 RETURNING,插入语句的 executemany 速度也似乎非常缓慢。
新功能的性能为每个驱动程序的 INSERT ORM 对象的性能提供了几乎横跨所有板块的数量级的性能提升,如下表所示,大多数情况下特定于 RETURNING 的使用,通常不支持 executemany()。
psycopg2 的“快速执行助手”方法包括将具有单个参数集的 INSERT…RETURNING 语句转换为一个插入多个参数集的单个语句,使用多个“VALUES…”子句,以便它可以一次容纳多个参数集。然后,参数集通常被批处理成一组 1000 或类似的参数集,以便没有单个 INSERT 语句过于大,并且 INSERT 语句然后针对每个参数批次调用,而不是针对每个单独的参数集。通过 RETURNING 返回主键值和服务器默认值,这仍然会在每个语句执行时使用 cursor.execute()
调用,而不是 cursor.executemany()
。
这允许一次插入许多行,同时还能够返回新生成的主键值以及 SQL 和服务器默认值。历史上,SQLAlchemy 一直需要对每个参数集调用一条语句,因为它依赖于诸如 cursor.lastrowid
等 Python DBAPI 功能,这些功能不支持多行。
由于现在大多数数据库都提供了 RETURNING(尤其是 MySQL 是一个明显的例外,因为 MariaDB 支持它),新的更改将 psycopg2 的“快速执行助手”方法推广到支持 RETURNING 的所有方言,现在包括 SQlite 和 MariaDB,并且对于没有其他方法来执行“executemany 加 RETURNING”的方言,包括 SQLite、MariaDB 和所有 PG 驱动程序。用于 Oracle 支持 RETURNING 的 cx_Oracle 和 oracledb 驱动程序会在本机支持 executemany,这也已经实现了相应的性能改进。由于 SQLite 和 MariaDB 现在提供 RETURNING 支持,ORM 对cursor.lastrowid
的使用几乎成为历史,只有 MySQL 仍然依赖于它。
对于不使用 RETURNING 的 INSERT 语句,对于大多数后端,都使用传统的 executemany()行为,当前的例外是 psycopg2,它的 executemany()性能总体上非常慢,并且仍然受到“insertmanyvalues”方法的改进。
基准测试
SQLAlchemy 在examples/
目录中包含一个性能套件,在这里我们可以利用bulk_insert
套件以不同的方式使用 Core 和 ORM 来对插入多行的 INSERT 进行基准测试。
对于下面的测试,我们插入了100,000 个对象,在所有情况下,我们实际上都有 100,000 个真实的 Python ORM 对象在内存中,无论是预先创建的还是动态生成的。除了 SQLite 之外的所有数据库都通过本地网络连接运行,而不是本地主机;这导致“较慢”的结果非常慢。
此功能改进的操作包括:
- 使用
Session.add()
和Session.add_all()
将对象添加到会话中的工作单元刷新。 - 新的 ORM 批量插入语句功能,改进了 SQLAlchemy 1.4 中首次引入的此功能的试验版本。
- 在批量操作中描述的
Session
“批量”操作,这些操作被上述 ORM 批量插入功能所取代。
为了对操作的规模有所了解,以下是使用test_flush_no_pk
性能套件的性能测量结果,该套件历史上代表了 SQLAlchemy 的最坏情况 INSERT 性能任务,其中需要插入没有主键值的对象,然后必须获取新生成的主键值,以便对象可以用于后续的 flush 操作,例如在关系中建立关系,刷新连接继承模型等:
@Profiler.profile def test_flush_no_pk(n): """INSERT statements via the ORM (batched with RETURNING if available), fetching generated row id""" session = Session(bind=engine) for chunk in range(0, n, 1000): session.add_all( [ Customer( name="customer name %d" % i, description="customer description %d" % i, ) for i in range(chunk, chunk + 1000) ] ) session.flush() session.commit()
可以从任何 SQLAlchemy 源代码树中运行此测试,如下所示:
python -m examples.performance.bulk_inserts --test test_flush_no_pk
下表总结了最新的 1.4 系列 SQLAlchemy 与 2.0 的性能测量结果,两者都运行相同的测试:
驱动程序 | SQLA 1.4 时间(秒) | SQLA 2.0 时间(秒) |
sqlite+pysqlite2 (内存) | 6.204843 | 3.554856 |
postgresql+asyncpg (网络) | 88.292285 | 4.561492 |
postgresql+psycopg (网络) | N/A (psycopg3) | 4.861368 |
mssql+pyodbc (网络) | 158.396667 | 4.825139 |
oracle+cx_Oracle (网络) | 92.603953 | 4.809520 |
mariadb+mysqldb (网络) | 71.705197 | 4.075377 |
注意
另外两个驱动程序在性能上没有变化;psycopg2 驱动程序,其在 SQLAlchemy 1.4 中已经实现了快速 executemany,以及 MySQL,它仍然不提供 RETURNING 支持:
驱动程序 | SQLA 1.4 时间(秒) | SQLA 2.0 时间(秒) |
postgresql+psycopg2 (网络) | 4.704876 | 4.699883 |
mysql+mysqldb (网络) | 77.281997 | 76.132995 |
变更摘要
以下项目列出了 2.0 版本中为使所有驱动程序达到这种状态所做的各项更改:
- SQLite 实现了 RETURNING - #6195
- MariaDB 实现了 RETURNING - #7011
- 修复 Oracle 的多行 RETURNING - #6245
- 使 insert() executemany() 支持尽可能多的方言的 RETURNING,通常使用 VALUES() - #6047
- 当对不支持的后端使用 RETURNING w/ executemany 时发出警告(当前没有 RETURNING 后端有此限制)- #7907
- ORM
Mapper.eager_defaults
参数现在默认为新设置"auto"
,当使用的后端支持带有“insertmanyvalues”的 RETURNING 时,将自动启用“eager defaults”用于 INSERT 语句。请参阅获取服务器生成的默认值以获取文档。
另请参阅
INSERT 语句的“Insert Many Values”行为 - 新功能的文档和背景以及如何配置它
基准测试
SQLAlchemy 在 examples/
目录中包含一个性能套件,在这里我们可以利用 bulk_insert
套件以不同方式使用 Core 和 ORM 来对插入多行的性能进行基准测试。
对于下面的测试,我们插入了100,000 个对象,在所有情况下,我们实际上在内存中有 100,000 个真实的 Python ORM 对象,要么事先创建,要么动态生成。除 SQLite 外的所有数据库都通过本地网络连接运行,而不是 localhost;这导致“较慢”的结果非常慢。
通过这个功能改进的操作包括:
- 通过
Session.add()
和Session.add_all()
向会话添加的对象的工作单元刷新。 - 新的 ORM 批量插入语句功能,改进了首次在 SQLAlchemy 1.4 中引入的此功能的实验版本。
Session
中描述的“bulk”操作,已被上述 ORM 批量插入功能取代。
为了了解操作的规模,以下是使用test_flush_no_pk
性能套件进行的性能测量,这通常代表 SQLAlchemy 的最坏情况 INSERT 性能任务,其中需要 INSERT 没有主键值的对象,然后必须获取新生成的主键值,以便这些对象可以用于后续的 flush 操作,比如在关系中建立,刷新加入继承模型等:
@Profiler.profile def test_flush_no_pk(n): """INSERT statements via the ORM (batched with RETURNING if available), fetching generated row id""" session = Session(bind=engine) for chunk in range(0, n, 1000): session.add_all( [ Customer( name="customer name %d" % i, description="customer description %d" % i, ) for i in range(chunk, chunk + 1000) ] ) session.flush() session.commit()
可以从任何 SQLAlchemy 源代码树中运行此测试:
python -m examples.performance.bulk_inserts --test test_flush_no_pk
下表总结了最新的 SQLAlchemy 1.4 系列与 2.0 之间的性能测量结果,两者都运行相同的测试:
驱动程序 | SQLA 1.4 时间(秒) | SQLA 2.0 时间(秒) |
sqlite+pysqlite2 (内存) | 6.204843 | 3.554856 |
postgresql+asyncpg (网络) | 88.292285 | 4.561492 |
postgresql+psycopg (网络) | N/A (psycopg3) | 4.861368 |
mssql+pyodbc (网络) | 158.396667 | 4.825139 |
oracle+cx_Oracle (网络) | 92.603953 | 4.809520 |
mariadb+mysqldb (网络) | 71.705197 | 4.075377 |
注意
另外两个驱动程序在性能上没有变化;对于已在 SQLAlchemy 1.4 中实现了快速 executemany 的 psycopg2 驱动程序以及继续不提供 RETURNING 支持的 MySQL:
驱动程序 | SQLA 1.4 时间(秒) | SQLA 2.0 时间(秒) |
postgresql+psycopg2 (网络) | 4.704876 | 4.699883 |
mysql+mysqldb (网络) | 77.281997 | 76.132995 |
变更摘要
以下项目列出了 2.0 中进行的各项更改,以使所有驱动程序达到此状态:
- 为 SQLite 实现了 RETURNING - #6195
- 为 MariaDB 实现了 RETURNING - #7011
- 修复了 Oracle 的多行 RETURNING - #6245
- 使 insert() executemany()支持尽可能多的方言,通常使用 VALUES() - #6047
- 当用于不支持的后端时发出警告 RETURNING w/ executemany(当前没有 RETURNING 后端具有此限制)- #7907
- ORM
Mapper.eager_defaults
参数现在默认为一个新设置"auto"
, 当使用的后端支持带有“insertmanyvalues”的 RETURNING 时,会自动为 INSERT 语句启用“急切默认”。请参阅获取服务器生成的默认值了解文档。
另请参阅
INSERT 语句的“Insert Many Values”行为 - 新功能的文档和背景,以及如何配置它
启用 ORM 的插入、更新和删除语句,带有 ORM RETURNING
SQLAlchemy 1.4 将遗留的Query
对象的特性移植到 2.0 样式执行,这意味着Select
构造可以传递给Session.execute()
以传递 ORM 结果。还增加了对Update
和Delete
的支持,以便它们可以提供Query.update()
和Query.delete()
的实现。
主要缺失的元素是对Insert
构造的支持。1.4 文档通过使用Select.from_statement()
的一些“插入”和“更新”配方来解决这个问题,将 RETURNING 集成到 ORM 上下文中。2.0 现在通过将Insert
的直接支持整合为增强版本的Session.bulk_insert_mappings()
方法以及对所有 DML 结构的完整 ORM RETURNING 支持来完全填补了这一空白。
带有 RETURNING 的批量插入
Insert
可以传递给 Session.execute()
,可以带有或不带有 Insert.returning()
,当传递给一个单独的参数列表时,将调用与以前由 Session.bulk_insert_mappings()
实现的相同的过程,同时增强了附加功能。这将优化使用新的快速插入功能的行批处理,同时还添加了对异构参数集和多表映射(如联合表继承)的支持:
>>> users = session.scalars( ... insert(User).returning(User), ... [ ... {"name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"name": "sandy", "fullname": "Sandy Cheeks"}, ... {"name": "patrick", "fullname": "Patrick Star"}, ... {"name": "squidward", "fullname": "Squidward Tentacles"}, ... {"name": "ehkrabs", "fullname": "Eugene H. Krabs"}, ... ], ... ) >>> print(users.all()) [User(name='spongebob', fullname='Spongebob Squarepants'), User(name='sandy', fullname='Sandy Cheeks'), User(name='patrick', fullname='Patrick Star'), User(name='squidward', fullname='Squidward Tentacles'), User(name='ehkrabs', fullname='Eugene H. Krabs')]
RETURNING 支持所有这些用例,ORM 将从多个语句调用中构造完整的结果集。
另请参阅
ORM 大规模插入语句
大规模 UPDATE
与 Insert
类似,将 Update
构造传递给包含主键值的参数列表的 Session.execute()
将调用之前由 Session.bulk_update_mappings()
方法支持的相同过程。但是,该功能不支持 RETURNING,因为它使用一个 SQL UPDATE 语句,该语句使用 DBAPI 的 executemany 调用:
>>> from sqlalchemy import update >>> session.execute( ... update(User), ... [ ... {"id": 1, "fullname": "Spongebob Squarepants"}, ... {"id": 3, "fullname": "Patrick Star"}, ... ], ... )
另请参阅
按主键进行 ORM 大规模 UPDATE
插入/ upsert … VALUES … RETURNING
使用 Insert
时,可在 Insert.values()
中包含一组参数,其中可能包括 SQL 表达式。此外,还支持 SQLite、PostgreSQL 和 MariaDB 的 upsert 变体。这些语句现在可以包含 Insert.returning()
子句,其中包括列表达式或完整的 ORM 实体:
>>> from sqlalchemy.dialects.sqlite import insert as sqlite_upsert >>> stmt = sqlite_upsert(User).values( ... [ ... {"name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"name": "sandy", "fullname": "Sandy Cheeks"}, ... {"name": "patrick", "fullname": "Patrick Star"}, ... {"name": "squidward", "fullname": "Squidward Tentacles"}, ... {"name": "ehkrabs", "fullname": "Eugene H. Krabs"}, ... ] ... ) >>> stmt = stmt.on_conflict_do_update( ... index_elements=[User.name], set_=dict(fullname=stmt.excluded.fullname) ... ) >>> result = session.scalars(stmt.returning(User)) >>> print(result.all()) [User(name='spongebob', fullname='Spongebob Squarepants'), User(name='sandy', fullname='Sandy Cheeks'), User(name='patrick', fullname='Patrick Star'), User(name='squidward', fullname='Squidward Tentacles'), User(name='ehkrabs', fullname='Eugene H. Krabs')]
另请参阅
使用每行 SQL 表达式进行 ORM 大规模插入
ORM “upsert” 语句
ORM UPDATE / DELETE with WHERE … RETURNING
SQLAlchemy 1.4 还对与update()
和delete()
构造一起使用时与Session.execute()
一起使用 RETURNING 功能提供了一些有限的支持。现在,此支持已经升级为完全本地化,包括fetch
同步策略也可以继续进行,无论是否明确使用 RETURNING:
>>> from sqlalchemy import update >>> stmt = ( ... update(User) ... .where(User.name == "squidward") ... .values(name="spongebob") ... .returning(User) ... ) >>> result = session.scalars(stmt, execution_options={"synchronize_session": "fetch"}) >>> print(result.all())
另请参阅
使用自定义 WHERE 条件的 ORM UPDATE 和 DELETE
使用 RETURNING 进行 UPDATE/DELETE 和自定义 WHERE 条件
改进的 ORM UPDATE / DELETE 的synchronize_session
行为
synchronize_session 的默认策略现在是一个新值"auto"
。此策略将尝试使用"evaluate"
策略,然后自动回退到"fetch"
策略。除了 MySQL / MariaDB 之外的所有后端,"fetch"
使用 RETURNING 在同一语句中获取 UPDATE/DELETE 的主键标识符,因此通常比以前的版本更有效(在 1.4 中,RETURNING 仅适用于 PostgreSQL、SQL Server)。
另请参阅
选择同步策略
变更摘要
新的 ORM DML 带有 RETURNING 功能的列出的票据:
- 将 ORM 级别的
insert()
转换为在 ORM 上下文中解释values()
- #7864 - 评估
dml.returning(Entity)
的可行性,以提供 ORM 表达式,自动应用select().from_statement
等效 - #7865 - 给定 ORM 插入,尝试沿用批量方法,关于继承 - #8360
带有 RETURNING 的批量插入
Insert
可以传递给Session.execute()
,可以带有或不带有Insert.returning()
,当与单独的参数列表一起传递时,将调用与以前由Session.bulk_insert_mappings()
实现的相同过程,同时增加了额外的增强功能。这将优化行的批处理,利用新的快速插入多行功能,同时还支持异构参数集和多表映射,如联合表继承:
>>> users = session.scalars( ... insert(User).returning(User), ... [ ... {"name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"name": "sandy", "fullname": "Sandy Cheeks"}, ... {"name": "patrick", "fullname": "Patrick Star"}, ... {"name": "squidward", "fullname": "Squidward Tentacles"}, ... {"name": "ehkrabs", "fullname": "Eugene H. Krabs"}, ... ], ... ) >>> print(users.all()) [User(name='spongebob', fullname='Spongebob Squarepants'), User(name='sandy', fullname='Sandy Cheeks'), User(name='patrick', fullname='Patrick Star'), User(name='squidward', fullname='Squidward Tentacles'), User(name='ehkrabs', fullname='Eugene H. Krabs')]
对于所有这些用例,都支持 RETURNING,其中 ORM 将从多个语句调用构造完整的结果集。
另请参阅
ORM 批量插入语句
批量更新
与Insert
类似,将Update
构造与包含主键值的参数列表一起传递给Session.execute()
将调用与之前由Session.bulk_update_mappings()
方法支持的相同过程。但是,此功能不支持 RETURNING,因为它使用了通过 DBAPI executemany 调用的 SQL UPDATE 语句:
>>> from sqlalchemy import update >>> session.execute( ... update(User), ... [ ... {"id": 1, "fullname": "Spongebob Squarepants"}, ... {"id": 3, "fullname": "Patrick Star"}, ... ], ... )
另请参阅
按主键批量更新的 ORM UPDATE
INSERT / upsert … VALUES … RETURNING
当使用Insert
与Insert.values()
时,参数集合可以包含 SQL 表达式。此外,还支持 SQLite、PostgreSQL 和 MariaDB 等数据库的 upsert 变体。这些语句现在可以包括带有列表达式或完整 ORM 实体的Insert.returning()
子句:
>>> from sqlalchemy.dialects.sqlite import insert as sqlite_upsert >>> stmt = sqlite_upsert(User).values( ... [ ... {"name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"name": "sandy", "fullname": "Sandy Cheeks"}, ... {"name": "patrick", "fullname": "Patrick Star"}, ... {"name": "squidward", "fullname": "Squidward Tentacles"}, ... {"name": "ehkrabs", "fullname": "Eugene H. Krabs"}, ... ] ... ) >>> stmt = stmt.on_conflict_do_update( ... index_elements=[User.name], set_=dict(fullname=stmt.excluded.fullname) ... ) >>> result = session.scalars(stmt.returning(User)) >>> print(result.all()) [User(name='spongebob', fullname='Spongebob Squarepants'), User(name='sandy', fullname='Sandy Cheeks'), User(name='patrick', fullname='Patrick Star'), User(name='squidward', fullname='Squidward Tentacles'), User(name='ehkrabs', fullname='Eugene H. Krabs')]
另请参阅
使用每行 SQL 表达式的 ORM 批量插入
ORM “upsert” 语句
带 WHERE … RETURNING 的 ORM UPDATE / DELETE
SQLAlchemy 1.4 也对与update()
和delete()
构造一起使用时与Session.execute()
一起使用 RETURNING 功能提供了一些有限支持。此支持现已升级为完全本机,包括fetch
同步策略也可以继续进行,无论是否存在显式使用 RETURNING:
>>> from sqlalchemy import update >>> stmt = ( ... update(User) ... .where(User.name == "squidward") ... .values(name="spongebob") ... .returning(User) ... ) >>> result = session.scalars(stmt, execution_options={"synchronize_session": "fetch"}) >>> print(result.all())
另请参阅
使用自定义 WHERE 条件的 ORM UPDATE 和 DELETE
使用 UPDATE/DELETE 和自定义 WHERE 条件的 RETURNING
ORM UPDATE / DELETE 的改进synchronize_session
行为
synchronize_session 的默认策略现在是一个新值"auto"
。此策略将尝试使用"evaluate"
策略,然后自动回退到"fetch"
策略。对于除 MySQL / MariaDB 之外的所有后端,"fetch"
使用 RETURNING 在同一语句中获取 UPDATE/DELETE 的主键标识符,因此通常比以前的版本更有效率(在 1.4 中,RETURNING 仅适用于 PostgreSQL,SQL Server)。
另请参阅
选择同步策略
变更摘要
新 ORM DML 的带有 RETURNING 特性的已列出的票证:
- 将 ORM 级别的
insert()
转换为在 ORM 上下文中解释values()
- #7864 - 评估 dml.returning(Entity)提供 ORM 表达式的可行性,自动应用 select().from_statement 等效 - #7865
- 给定 ORM 插入,尝试沿用批量方法,即继承关系- #8360
新的“只写”关系策略取代了“动态”
lazy="dynamic"
加载策略已经过时,因为它是硬编码的,使用了遗留的Query
。这种加载策略既不兼容 asyncio,而且还有许多行为会隐式地迭代其内容,这些行为背离了“动态”关系最初的目的,即针对不应在任何时候隐式完全加载到内存中的非常大的集合。
“动态”策略现已由新策略lazy="write_only"
取代。可以使用relationship()
的relationship.lazy
参数进行“只写”配置,或者在使用类型注释映射时,将WriteOnlyMapped
注释指示为映射样式:
from sqlalchemy.orm import WriteOnlyMapped class Base(DeclarativeBase): pass class Account(Base): __tablename__ = "account" id: Mapped[int] = mapped_column(primary_key=True) identifier: Mapped[str] account_transactions: WriteOnlyMapped["AccountTransaction"] = relationship( cascade="all, delete-orphan", passive_deletes=True, order_by="AccountTransaction.timestamp", ) class AccountTransaction(Base): __tablename__ = "account_transaction" id: Mapped[int] = mapped_column(primary_key=True) account_id: Mapped[int] = mapped_column( ForeignKey("account.id", ondelete="cascade") ) description: Mapped[str] amount: Mapped[Decimal] timestamp: Mapped[datetime] = mapped_column(default=func.now())
写入仅映射集合类似于lazy="dynamic"
,因为集合可以提前分配,并且还具有WriteOnlyCollection.add()
和WriteOnlyCollection.remove()
等方法,以逐个项目的方式修改集合:
new_account = Account( identifier="account_01", account_transactions=[ AccountTransaction(description="initial deposit", amount=Decimal("500.00")), AccountTransaction(description="transfer", amount=Decimal("1000.00")), AccountTransaction(description="withdrawal", amount=Decimal("-29.50")), ], ) new_account.account_transactions.add( AccountTransaction(description="transfer", amount=Decimal("2000.00")) )
在数据库加载方面的主要区别在于,该集合没有直接从数据库加载对象的能力;相反,使用 SQL 构造方法,如 WriteOnlyCollection.select()
来生成 SQL 构造,比如 Select
,然后使用 2.0 风格 显式加载所需对象:
account_transactions = session.scalars( existing_account.account_transactions.select() .where(AccountTransaction.amount < 0) .limit(10) ).all()
WriteOnlyCollection
也与新的 ORM bulk dml 功能集成,包括支持带有 WHERE 条件的批量 INSERT 和 UPDATE/DELETE,所有这些都包括 RETURNING 支持。请参阅完整文档 Write Only Relationships。
另请参阅
Write Only Relationships
动态关系的新 pep-484 / 类型注释映射支持
尽管“动态”关系在 2.0 中是遗留的,但由于这些模式预计具有较长的生命周期,类型注释映射 现在已添加到“动态”关系中,与新的 lazy="write_only"
方法可用的方式相同,使用 DynamicMapped
注释:
from sqlalchemy.orm import DynamicMapped class Base(DeclarativeBase): pass class Account(Base): __tablename__ = "account" id: Mapped[int] = mapped_column(primary_key=True) identifier: Mapped[str] account_transactions: DynamicMapped["AccountTransaction"] = relationship( cascade="all, delete-orphan", passive_deletes=True, order_by="AccountTransaction.timestamp", ) class AccountTransaction(Base): __tablename__ = "account_transaction" id: Mapped[int] = mapped_column(primary_key=True) account_id: Mapped[int] = mapped_column( ForeignKey("account.id", ondelete="cascade") ) description: Mapped[str] amount: Mapped[Decimal] timestamp: Mapped[datetime] = mapped_column(default=func.now())
上述映射将提供一个 Account.account_transactions
集合,其类型为 AppenderQuery
集合类型,包括其元素类型,例如 AppenderQuery[AccountTransaction]
。然后允许迭代和查询产生类型为 AccountTransaction
的对象。
另请参阅
动态关系加载器
动态关系的新 pep-484 / 类型注释映射支持
尽管“动态”关系在 2.0 中是遗留的,但由于这些模式预计具有较长的生命周期,类型注释映射 现在已添加到“动态”关系中,与新的 lazy="write_only"
方法可用的方式相同,使用 DynamicMapped
注释:
from sqlalchemy.orm import DynamicMapped class Base(DeclarativeBase): pass class Account(Base): __tablename__ = "account" id: Mapped[int] = mapped_column(primary_key=True) identifier: Mapped[str] account_transactions: DynamicMapped["AccountTransaction"] = relationship( cascade="all, delete-orphan", passive_deletes=True, order_by="AccountTransaction.timestamp", ) class AccountTransaction(Base): __tablename__ = "account_transaction" id: Mapped[int] = mapped_column(primary_key=True) account_id: Mapped[int] = mapped_column( ForeignKey("account.id", ondelete="cascade") ) description: Mapped[str] amount: Mapped[Decimal] timestamp: Mapped[datetime] = mapped_column(default=func.now())
上述映射将提供一个类型为返回AppenderQuery
集合类型的Account.account_transactions
集合,包括其元素类型,例如AppenderQuery[AccountTransaction]
。这样就允许迭代和查询产生类型为AccountTransaction
的对象。
另请参阅
动态关系加载器
SqlAlchemy 2.0 中文文档(五十七)(8)https://developer.aliyun.com/article/1563170