使用大型集合


relationship() 的默认行为是完全加载 将集合的内容放入内存,根据配置的 loader 策略,该策略控制何时以及如何从数据库中加载这些内容。相关集合不仅可以在访问或预先加载时加载到内存中,而且在大多数情况下,当集合本身发生突变时,以及在工作单元系统要删除拥有对象的情况下,都需要 population。


当相关集合可能非常大时,在任何情况下都将此类集合填充到内存中可能不可行,因为该作可能会过度消耗时间、网络和内存资源。


本节包括旨在允许 relationship() 的 API 功能 用于大型集合,同时保持足够的性能。


只写关系


只写加载程序策略是配置 relationship() 将保持可写状态,但不会将其内容加载到内存中。现代类型注释的声明式形式的只写 ORM 配置如下所示:

>>> from decimal import Decimal
>>> from datetime import datetime

>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy import func
>>> from sqlalchemy.orm import DeclarativeBase
>>> from sqlalchemy.orm import Mapped
>>> from sqlalchemy.orm import mapped_column
>>> from sqlalchemy.orm import relationship
>>> from sqlalchemy.orm import Session
>>> 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",
...     )
...
...     def __repr__(self):
...         return f"Account(identifier={self.identifier!r})"

>>> 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())
...
...     def __repr__(self):
...         return (
...             f"AccountTransaction(amount={self.amount:.2f}, "
...             f"timestamp={self.timestamp.isoformat()!r})"
...         )
...
...     __mapper_args__ = {"eager_defaults": True}


在上面,account_transactions关系不是使用普通的 Mapped 注解来配置的,而是使用 WriteOnlyMapped 类型注解,在运行时,该注解将分配 lazy=“write_only” 添加到目标 relationship() 中。WriteOnlyMapped 批注是 Mapped 批注的另一种形式,它指示在对象的实例上使用 WriteOnlyCollection 集合类型。


上述 relationship() 配置还包括几个元素,这些元素特定于在删除 Account 对象以及从 account_transactions 集合。这些元素是:


2.0 版本中的新功能: 添加了 “Write only” 关系加载器。


创建并持久化新的 Write Only 集合


只写集合允许仅针对瞬态待处理对象将集合作为一个整体直接分配。通过上面的映射,这表明我们可以创建一个新的账户 对象,其中包含要添加到 SessionAccountTransaction 对象序列。任何 Python 可迭代对象都可以用作要启动的对象源,下面我们使用 Python 列表

>>> 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")),
...     ],
... )

>>> with Session(engine) as session:
...     session.add(new_account)
...     session.commit()
BEGIN (implicit) INSERT INTO account (identifier) VALUES (?) [...] ('account_01',) INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp [... (insertmanyvalues) 1/3 (ordered; batch not supported)] (1, 'initial deposit', 500.0) INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp [insertmanyvalues 2/3 (ordered; batch not supported)] (1, 'transfer', 1000.0) INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp [insertmanyvalues 3/3 (ordered; batch not supported)] (1, 'withdrawal', -29.5) COMMIT


一旦对象是数据库持久化的(即在持久化detached 状态),该集合能够使用新项进行扩展,并且能够删除单个项。但是,不能再使用完整的替换集合重新分配该集合,因为此类作需要将以前的集合完全加载到内存中,以便将旧条目与新条目进行协调:

>>> new_account.account_transactions = [
...     AccountTransaction(description="some transaction", amount=Decimal("10.00"))
... ]
Traceback (most recent call last):
...
sqlalchemy.exc.InvalidRequestError: Collection "Account.account_transactions" does not
support implicit iteration; collection replacement operations can't be used


将新项目添加到现有集合


对于持久对象的只写集合,使用工作进程单元对集合的修改只能通过使用 WriteOnlyCollection.add() 进行。 WriteOnlyCollection.add_all()WriteOnlyCollection.remove() 方法:

>>> from sqlalchemy import select
>>> session = Session(engine, expire_on_commit=False)
>>> existing_account = session.scalar(select(Account).filter_by(identifier="account_01"))
BEGIN (implicit) SELECT account.id, account.identifier FROM account WHERE account.identifier = ? [...] ('account_01',)
>>> existing_account.account_transactions.add_all( ... [ ... AccountTransaction(description="paycheck", amount=Decimal("2000.00")), ... AccountTransaction(description="rent", amount=Decimal("-800.00")), ... ] ... ) >>> session.commit()
INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp [... (insertmanyvalues) 1/2 (ordered; batch not supported)] (1, 'paycheck', 2000.0) INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, timestamp [insertmanyvalues 2/2 (ordered; batch not supported)] (1, 'rent', -800.0) COMMIT


上面添加的项目保存在 Session 直到下一次刷新,此时它们将被 INSERT 到数据库中,假设添加的对象以前是瞬态的。


查询 Item


WriteOnlyCollection 在任何时候都不会存储对集合当前内容的引用,也没有任何行为会直接向数据库发出 SELECT 以加载它们;最重要的假设是该集合可能包含数千或数百万行,并且永远不应作为任何其他作的副作用完全加载到内存中。


相反,WriteOnlyCollection 包括 SQL 生成帮助程序,例如 WriteOnlyCollection.select(),它将生成一个 Select 结构,该结构预先配置了当前父行的正确 WHERE / FROM 标准,然后可以进一步修改该结构以 SELECT 所需的任何行范围,以及使用服务器端游标等功能调用适用于希望以内存高效的方式迭代完整集合的进程。


生成的语句如下所示。请注意,它还包括 ORDER BY 标准,在示例映射中由 relationship.order_by relationship() 的参数;如果未配置参数,则将省略此条件:

>>> print(existing_account.account_transactions.select())
SELECT account_transaction.id, account_transaction.account_id, account_transaction.description, account_transaction.amount, account_transaction.timestamp FROM account_transaction WHERE :param_1 = account_transaction.account_id ORDER BY account_transaction.timestamp


我们可以将这个 Select 结构与 Session 一起使用 为了查询 AccountTransaction 对象,最简单的方法是使用 Session.scalars() 方法,该方法将返回一个 Result,该 直接生成 ORM 对象。通常(但不是必需的)是 进一步修改 select 以限制返回的记录;在下面的示例中,添加了仅加载 “debit” 账户交易的附加 WHERE 标准,以及仅检索前 10 行的 “LIMIT 10” 标准:

>>> account_transactions = session.scalars(
...     existing_account.account_transactions.select()
...     .where(AccountTransaction.amount < 0)
...     .limit(10)
... ).all()
BEGIN (implicit) SELECT account_transaction.id, account_transaction.account_id, account_transaction.description, account_transaction.amount, account_transaction.timestamp FROM account_transaction WHERE ? = account_transaction.account_id AND account_transaction.amount < ? ORDER BY account_transaction.timestamp LIMIT ? OFFSET ? [...] (1, 0, 10, 0)
>>> print(account_transactions) [AccountTransaction(amount=-29.50, timestamp='...'), AccountTransaction(amount=-800.00, timestamp='...')]


删除项目


持久化 state 可以使用 WriteOnlyCollection.remove() 方法标记为从集合中删除。当作继续进行时,flush 进程将隐式地认为该对象已经是集合的一部分。下面的示例说明了删除单个 AccountTransaction 项,根据级联设置,这会导致该行的 DELETE:

>>> existing_transaction = account_transactions[0]
>>> existing_account.account_transactions.remove(existing_transaction)
>>> session.commit()
DELETE FROM account_transaction WHERE account_transaction.id = ? [...] (3,) COMMIT


与任何 ORM 映射的集合一样,对象删除可以继续进行到 取消对象与集合的关联,同时将对象保留在 数据库,或者可能会根据 delete-orphan 配置。


删除集合而不删除涉及将一对关系的外键列设置为 NULL,或者 删除 多对多关系。


批量 INSERT 新项


WriteOnlyCollection 可以生成 DML 构造,例如 Insert 对象,可以在 ORM 上下文中使用 生成批量插入行为。 请参阅该部分 ORM 批量插入语句,了解 ORM 批量插入的概述。


一对多集合


仅对于常规的一对多集合WriteOnlyCollection.insert() 方法将生成一个 Insert 结构,该结构是使用与父对象对应的 VALUES 标准预先建立的。由于此 VALUES 标准完全针对相关表,因此该语句可用于 INSERT 新行,这些新行将同时成为相关集合中的新记录:

>>> session.execute(
...     existing_account.account_transactions.insert(),
...     [
...         {"description": "transaction 1", "amount": Decimal("47.50")},
...         {"description": "transaction 2", "amount": Decimal("-501.25")},
...         {"description": "transaction 3", "amount": Decimal("1800.00")},
...         {"description": "transaction 4", "amount": Decimal("-300.00")},
...     ],
... )
BEGIN (implicit) INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP) [...] [(1, 'transaction 1', 47.5), (1, 'transaction 2', -501.25), (1, 'transaction 3', 1800.0), (1, 'transaction 4', -300.0)] <...>
>>> session.commit() COMMIT


多对多集合


对于多对多集合,两个类之间的关系 涉及第三个表,该表使用 relationship.secondary 参数。 要使用 WriteOnlyCollection 中,新记录可以单独批量插入 首先,使用 RETURNING 检索,然后将这些记录传递给 WriteOnlyCollection.add_all() 方法,其中工作单元进程将继续将它们持久化为集合的一部分。


假设一个类 BankAudit 引用了许多 AccountTransaction 使用多对多表的记录:

>>> from sqlalchemy import Table, Column
>>> audit_to_transaction = Table(
...     "audit_transaction",
...     Base.metadata,
...     Column("audit_id", ForeignKey("audit.id", ondelete="CASCADE"), primary_key=True),
...     Column(
...         "transaction_id",
...         ForeignKey("account_transaction.id", ondelete="CASCADE"),
...         primary_key=True,
...     ),
... )
>>> class BankAudit(Base):
...     __tablename__ = "audit"
...     id: Mapped[int] = mapped_column(primary_key=True)
...     account_transactions: WriteOnlyMapped["AccountTransaction"] = relationship(
...         secondary=audit_to_transaction, passive_deletes=True
...     )


为了说明这两个作,我们添加了更多的 AccountTransaction 对象 using bulk insert,我们使用 RETURNING 通过添加 returning(AccountTransaction) 到批量 INSERT 语句(请注意,我们也可以很容易地使用现有的 AccountTransaction 对象):

>>> new_transactions = session.scalars(
...     existing_account.account_transactions.insert().returning(AccountTransaction),
...     [
...         {"description": "odd trans 1", "amount": Decimal("50000.00")},
...         {"description": "odd trans 2", "amount": Decimal("25000.00")},
...         {"description": "odd trans 3", "amount": Decimal("45.00")},
...     ],
... ).all()
BEGIN (implicit) INSERT INTO account_transaction (account_id, description, amount, timestamp) VALUES (?, ?, ?, CURRENT_TIMESTAMP), (?, ?, ?, CURRENT_TIMESTAMP), (?, ?, ?, CURRENT_TIMESTAMP) RETURNING id, account_id, description, amount, timestamp [...] (1, 'odd trans 1', 50000.0, 1, 'odd trans 2', 25000.0, 1, 'odd trans 3', 45.0)


准备好 AccountTransaction 对象列表后, WriteOnlyCollection.add_all() 方法用于一次将多行与新的 BankAudit 对象相关联:

>>> bank_audit = BankAudit()
>>> session.add(bank_audit)
>>> bank_audit.account_transactions.add_all(new_transactions)
>>> session.commit()
INSERT INTO audit DEFAULT VALUES [...] () INSERT INTO audit_transaction (audit_id, transaction_id) VALUES (?, ?) [...] [(1, 10), (1, 11), (1, 12)] COMMIT


批量 UPDATE 和 DELETE 项


WriteOnlyCollection 可以生成 预先建立 WHERE 标准的 Select 结构,它还可以生成具有相同 WHERE 标准的 UpdateDelete 结构,以允许对大型集合中的元素进行面向标准的 UPDATE 和 DELETE 语句。


一对多集合


与 INSERT 一样,此功能最直接地使用 到许多集合


在下面的示例中,WriteOnlyCollection.update() 方法用于生成针对集合中的元素发出的 UPDATE 语句,查找 “amount” 等于 -800 的行,并将 200 的数量添加到它们中:

>>> session.execute(
...     existing_account.account_transactions.update()
...     .values(amount=AccountTransaction.amount + 200)
...     .where(AccountTransaction.amount == -800),
... )
BEGIN (implicit) UPDATE account_transaction SET amount=(account_transaction.amount + ?) WHERE ? = account_transaction.account_id AND account_transaction.amount = ? [...] (200, 1, -800)
<...>


以类似的方式,WriteOnlyCollection.delete() 将生成一个以相同方式调用的 DELETE 语句:

>>> session.execute(
...     existing_account.account_transactions.delete().where(
...         AccountTransaction.amount.between(0, 30)
...     ),
... )
DELETE FROM account_transaction WHERE ? = account_transaction.account_id AND account_transaction.amount BETWEEN ? AND ? RETURNING id [...] (1, 0, 30) <...>


多对多集合


提示


此处的技术涉及多表 UPDATE 表达式,这些表达式稍微高级一些。


对于多对多集合的批量 UPDATE 和 DELETE,为了使 UPDATE 或 DELETE 语句与父对象的主键相关,关联表必须明确成为 UPDATE/DELETE 语句的一部分,这要求后端包括对非标准 SQL 语法的支持,或者在构造 UPDATE 或 DELETE 语句时执行额外的显式步骤。


对于支持多表版本的 UPDATE 的后端, 对于多对多集合,WriteOnlyCollection.update() 方法应该无需额外步骤即可工作,如以下示例所示,其中根据多对多 BankAudit.account_transactions 集合对 AccountTransaction 对象发出 UPDATE:

>>> session.execute(
...     bank_audit.account_transactions.update().values(
...         description=AccountTransaction.description + " (audited)"
...     )
... )
UPDATE account_transaction SET description=(account_transaction.description || ?) FROM audit_transaction WHERE ? = audit_transaction.audit_id AND account_transaction.id = audit_transaction.transaction_id RETURNING id [...] (' (audited)', 1)
<...>


上述语句自动使用 “UPDATE..FROM“语法,以命名其他audit_transaction table 的 WHERE 子句中。


要在多表语法不可用的情况下 UPDATE 或 DELETE 多对多集合,可以将多对多条件移动到 SELECT 中,例如,可以与 IN 结合使用以匹配行。WriteOnlyCollection 在这里仍然帮助我们,因为我们使用 WriteOnlyCollection.select() 方法为我们生成这个 SELECT,利用 Select.with_only_columns() 方法生成一个标量子查询

>>> from sqlalchemy import update
>>> subq = bank_audit.account_transactions.select().with_only_columns(AccountTransaction.id)
>>> session.execute(
...     update(AccountTransaction)
...     .values(description=AccountTransaction.description + " (audited)")
...     .where(AccountTransaction.id.in_(subq))
... )
UPDATE account_transaction SET description=(account_transaction.description || ?) WHERE account_transaction.id IN (SELECT account_transaction.id FROM audit_transaction WHERE ? = audit_transaction.audit_id AND account_transaction.id = audit_transaction.transaction_id) RETURNING id [...] (' (audited)', 1) <...>


只写集合 - API 文档


对象名称

描述

WriteOnlyCollection


Write-only 集合,可以将更改同步到属性事件系统中。

WriteOnlyMapped


表示 “write only” 关系的 ORM 映射属性类型。


sqlalchemy.orm 中。WriteOnlyCollection


Write-only 集合,可以将更改同步到属性事件系统中。


WriteOnlyCollection 通过使用 “write_only” 延迟加载策略在映射中使用 relationship() 的有关此配置的背景信息,请参阅Write Only Relationships


2.0 版的新Function。


另请参阅


仅写入关系


类签名


sqlalchemy.orm.WriteOnlyCollection sqlalchemy.orm.writeonly.AbstractCollectionWriter


method sqlalchemy.orm.WriteOnlyCollection. additem _T None(无)¶


向此 WriteOnlyCollection 添加项。


给定的 item 将在下一次刷新时根据父实例的集合持久化到数据库中。


方法 sqlalchemy.orm.WriteOnlyCollection. add_alliterator Iterable[_T] None


将项的可迭代对象添加到此 WriteOnlyCollection


给定的 items 将在下一次刷新时根据父实例的集合保存到数据库中。


method sqlalchemy.orm.WriteOnlyCollection. : delete 删除


生成一个 Delete,它将根据此实例本地 WriteOnlyCollection 引用行。


方法 sqlalchemy.orm.WriteOnlyCollection. insert 插入


对于一对多集合,生成一个 Insert ,该 将插入新行 this instance-local WriteOnlyCollection 的 WriteOnlyCollection 中。


此构造仅支持 Relationship 这不包括 relationship.secondary 参数。 对于引用多对多表的关系, 使用普通的批量插入技术生成新对象,然后 用于 AbstractCollectionWriter.add_all() 将它们与集合关联。


method sqlalchemy.orm.WriteOnlyCollection. removeitem _T None(无)¶


从此 WriteOnlyCollection 中删除项。


给定的项将在下一次刷新时从父实例的集合中删除。


方法 sqlalchemy.orm.WriteOnlyCollection. select)→ Select[元组[_T]


生成一个 Select 构造,该构造表示此实例本地 WriteOnlyCollection 中的行。


方法 sqlalchemy.orm.WriteOnlyCollection. update Update


生成一个 Update,它将根据此实例本地 WriteOnlyCollection 引用行。


sqlalchemy.orm 中。WriteOnlyMapped


表示 “write only” 关系的 ORM 映射属性类型。


WriteOnlyMapped 类型注释可以在 带注释的声明式表映射,以指示 lazy=“write_only” 加载器策略应用于特定的 relationship()


例如:

class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    addresses: WriteOnlyMapped[Address] = relationship(
        cascade="all,delete-orphan"
    )


有关背景信息,请参阅 Write Only Relationships 部分。


2.0 版的新Function。


另请参阅


只写关系 - 完整背景


DynamicMapped - 包括旧版查询支持


类签名


sqlalchemy.orm.WriteOnlyMapped sqlalchemy.orm.base._MappedAnnotationBase


动态关系加载器


旧版功能


“动态” 惰性加载器策略是 现在是本节中描述的 “write_only” 策略 仅写入关系


“dynamic” 策略从相关集合中生成旧版 Query 对象。但是,“动态”关系的一个主要缺点是,在几种情况下,集合将完全迭代,其中一些情况并不明显,只有根据具体情况进行仔细编程和测试才能防止这种情况。因此,对于真正的大型集合管理,应首选 WriteOnlyCollection


动态加载程序也与异步 I/O (asyncio) 不兼容 外延。它可以在有一些限制的情况下使用,如 Asyncio 动态准则,但同样是 应该首选与 asyncio 完全兼容的 WriteOnlyCollection


动态关系策略允许配置 relationship() 在实例上访问时,将返回一个遗留的 Query 对象来代替集合。这 然后可以进一步修改 Query,以便数据库 集合可以根据筛选条件进行迭代。返回的 Query 对象是 AppenderQuery 的一个实例,它结合了 Query 的加载和迭代行为以及 基本的集合突变方法,例如 AppenderQuery.append()AppenderQuery.remove() 来获取。


“动态” 加载器策略可以使用 DynamicMapped 使用类型注释的声明式形式进行配置 annotation 类:

from sqlalchemy.orm import DynamicMapped


class User(Base):
    __tablename__ = "user"

    id: Mapped[int] = mapped_column(primary_key=True)
    posts: DynamicMapped[Post] = relationship()


在上面,单个 User 对象上的 User.posts 集合将返回 AppenderQuery 对象,它是 Query 的子类,也支持基本的集合更改作:

jack = session.get(User, id)

# filter Jack's blog posts
posts = jack.posts.filter(Post.headline == "this is a post")

# apply array slices
posts = jack.posts[5:20]


动态关系通过 AppenderQuery.append()AppenderQuery.remove() 方法:

oldpost = jack.posts.filter(Post.headline == "old post").one()
jack.posts.remove(oldpost)

jack.posts.append(Post("new post"))


由于动态关系的读取端始终查询数据库,因此在刷新数据之前,对基础集合的更改将不可见。但是,只要在正在使用的 Session 上启用了 “autoflush”,每次集合即将发出查询时,都会自动发生这种情况。


动态关系加载器 - API


对象名称

描述


AppenderQuery 查询


支持基本集合存储作的动态查询。


动态映射


表示 “dynamic” 关系的 ORM 映射属性类型。


sqlalchemy.orm 中。AppenderQuery


支持基本集合存储作的动态查询。


AppenderQuery 上的方法包括 Query 以及用于集合持久性的其他方法。


类签名


sqlalchemy.orm.AppenderQuerysqlalchemy.orm.dynamic.AppenderMixinsqlalchemy.orm.Query


方法 sqlalchemy.orm.AppenderQuery 中。additem _T


继承自AppenderMixin 的 AppenderMixin.add() 方法


向此 AppenderQuery 添加一个项目。


给定的 item 将在下一次刷新时根据父实例的集合持久化到数据库中。


提供此方法是为了帮助提供与 WriteOnlyCollection 集合类的向前兼容性。


2.0 版的新Function。


方法 sqlalchemy.orm.AppenderQuery 中。add_alliterator Iterable[_T]


继承自AppenderMixinAppenderMixin.add_all() 方法


将项的可迭代对象添加到此 AppenderQuery 中。


给定的 items 将在下一次刷新时根据父实例的集合保存到数据库中。


提供此方法是为了帮助提供与 WriteOnlyCollection 集合类的向前兼容性。


2.0 版的新Function。


方法 sqlalchemy.orm.AppenderQuery 中。appenditem _T


继承自AppenderMixin 的 AppenderMixin.append() 方法


将项目追加到此 AppenderQuery


给定的 item 将在下一次刷新时根据父实例的集合持久化到数据库中。


方法 sqlalchemy.orm.AppenderQuery 中。count int


继承自AppenderMixin 的 AppenderMixin.count() 方法


返回此 Query 形成的 SQL 的行数 会回来。


这将为此 Query 生成 SQL,如下所示:

SELECT count(1) AS count_1 FROM (
    SELECT <rest of query follows...>
) AS anon_1


上面的 SQL 返回一行,即 count 函数的聚合值;Query.count() method 然后返回 那个 single integer 值。


警告


需要注意的是,count() 返回的值与此 Query 将从 .all() 方法等方法返回。当要求返回完整实体时,Query 对象将根据主键删除重复的条目,这意味着如果相同的主键值在结果中多次出现,则只会存在该主键的一个对象。这不适用于针对单个列的查询。


要对要计数的特定列进行精细控制,要跳过子查询的使用或 FROM 子句的其他控制,或者要使用其他聚合函数,请使用 expression.func 表达式与 Session.query() 结合使用,即:

from sqlalchemy import func

# count User records, without
# using a subquery.
session.query(func.count(User.id))

# return count of user "id" grouped
# by "name"
session.query(func.count(User.id)).group_by(User.name)

from sqlalchemy import distinct

# count distinct "name" values
session.query(func.count(distinct(User.name)))

方法 sqlalchemy.orm.AppenderQuery 中。extenditerator Iterable[_T]


继承自AppenderMixin 的 AppenderMixin.extend() 方法


将项的可迭代对象添加到此 AppenderQuery 中。


给定的 items 将在下一次刷新时根据父实例的集合保存到数据库中。


方法 sqlalchemy.orm.AppenderQuery 中。removeitem _T


继承自AppenderMixin 的 AppenderMixin.remove() 方法


从此 AppenderQuery 中删除一个项目。


给定的项将在下一次刷新时从父实例的集合中删除。


sqlalchemy.orm 中。动态映射


表示 “dynamic” 关系的 ORM 映射属性类型。


DynamicMapped 类型注释可以在 带注释的声明式表映射,以指示 lazy=“dynamic” 加载器策略应用于特定的 relationship()。


旧版功能


“动态” 惰性加载器策略是 现在是本节中描述的 “write_only” 策略 仅写入关系


例如:

class User(Base):
    __tablename__ = "user"
    id: Mapped[int] = mapped_column(primary_key=True)
    addresses: DynamicMapped[Address] = relationship(
        cascade="all,delete-orphan"
    )


有关背景信息,请参阅 Dynamic Relationship Loaders 部分。


2.0 版的新Function。


另请参阅


动态关系加载器 - 完整背景


WriteOnlyMapped - 完全 2.0 样式版本


类签名


sqlalchemy.orm.DynamicMapped () sqlalchemy.orm.base._MappedAnnotationBase


设置 RaiseLoad


加载 “raise” 的关系将引发 InvalidRequestError,其中该属性通常会发出延迟加载:

class MyClass(Base):
    __tablename__ = "some_table"

    # ...

    children: Mapped[List[MyRelatedClass]] = relationship(lazy="raise")


在上面,如果之前未填充 children 集合,则对该集合的属性访问将引发异常。这包括读取访问,但对于集合也会影响写入访问,因为如果不先加载集合,就无法更改集合。这样做的基本原理是确保应用程序在特定上下文中不会发出任何意外的延迟加载。“raise” 策略不必阅读 SQL 日志来确定所有必要的属性都已预先加载,而是会导致未加载的属性在访问时立即引发。raise 策略也可以使用 raiseload() 作为查询选项使用 loader 选项。


使用被动删除


SQLAlchemy 中集合管理的一个重要方面是,当 对象被删除,则 SQLAlchemy 需要考虑 对象。这些对象需要 de-associated from the parent,对于一对多集合,这意味着 外键列设置为 NULL,或基于 cascade 设置,则可能希望为这些行发出 DELETE。


工作进程单元仅逐行考虑对象,这意味着 DELETE作意味着集合中的所有行都必须完全加载到 flush 进程内的内存中。这对于大型集合来说是不可行的,因此我们寻求依靠数据库自身的功能,使用外键 ON DELETE 规则自动更新或删除行,指示工作单元放弃实际需要加载这些行来处理它们。可以通过在 relationship() 结构上配置 relationship.passive_deletes 来指示工作单元以这种方式工作;还必须正确配置正在使用的外键约束。


有关完整“被动删除”配置的更多详细信息,请参阅将外键 ON DELETE 级联与 ORM 关系一起使用部分。