SqlAlchemy 2.0 中文文档(十三)(1)https://developer.aliyun.com/article/1562959
只写集合 - API 文档
对象名称 | 描述 |
WriteOnlyCollection | 只写集合可以将更改同步到属性事件系统中。 |
WriteOnlyMapped | 代表“只写”关系的 ORM 映射属性类型。 |
class sqlalchemy.orm.WriteOnlyCollection
只写集合可以将更改同步到属性事件系统中。
使用WriteOnlyCollection
在映射中使用"write_only"
延迟加载策略与relationship()
一起。有关此配置的背景,请参阅只写关系。
2.0 版本中的新功能。
另请参阅
只写关系
成员
add(), add_all(), delete(), insert(), remove(), select(), update()
类签名
类sqlalchemy.orm.WriteOnlyCollection
(sqlalchemy.orm.writeonly.AbstractCollectionWriter
)
method add(item: _T) → None
将一个项添加到此WriteOnlyCollection
中。
给定项将在下一个刷新时以父实例的集合的形式持久化到数据库中。
method add_all(iterator: Iterable[_T]) → None
将一个可迭代的项添加到此WriteOnlyCollection
中。
给定的项将在下一个刷新时以父实例的集合的形式持久化到数据库中。
method delete() → Delete
生成一个Delete
,该语句将以此实例本地的WriteOnlyCollection
的形式引用行。
method insert() → Insert
对于一对多的集合,生成一个Insert
,该语句将以此实例本地的WriteOnlyCollection
的形式插入新的行。
此构造仅支持不包括relationship.secondary
参数的Relationship
。对于指向多对多表的关系,请使用普通的批量插入技术来生成新对象,然后使用AbstractCollectionWriter.add_all()
将它们与集合关联起来。
method remove(item: _T) → None
从此WriteOnlyCollection
中移除一个项。
下一个刷新时,给定项将从父实例的集合中移除。
method select() → Select[Tuple[_T]]
生成一个Select
构造,表示此实例本地的WriteOnlyCollection
中的行。
method update() → Update
生成一个Update
,该语句将以此实例本地的WriteOnlyCollection
的形式引用行。
class sqlalchemy.orm.WriteOnlyMapped
表示“只写”关系的 ORM 映射属性类型。
WriteOnlyMapped
类型注释可以在带注释的声明性表映射中使用,以指示对于特定的relationship()
应使用lazy="write_only"
加载策略。
例如:
class User(Base): __tablename__ = "user" id: Mapped[int] = mapped_column(primary_key=True) addresses: WriteOnlyMapped[Address] = relationship( cascade="all,delete-orphan" )
有关背景,请参阅仅写关系部分。
2.0 版中的新功能。
请参阅还有
仅写关系 - 完整背景
DynamicMapped
- 包含遗留的Query
支持
类签名
类sqlalchemy.orm.WriteOnlyMapped
(sqlalchemy.orm.base._MappedAnnotationBase
) ## 动态关系加载器
遗留特性
“动态”延迟加载策略是现在在仅写关系部分中描述的“write_only”策略的遗留形式。
“动态”策略从相关集合中生成一个遗留的Query
对象。然而,“动态”关系的一个主要缺点是,有几种情况下集合会完全迭代,其中一些是不明显的,只能通过细心的编程和逐案的测试来预防。因此,对于真正大型集合管理,应优先考虑WriteOnlyCollection
。
动态加载器也与异步 I/O(asyncio)扩展不兼容。可以在一些限制下使用,如 Asyncio 动态指南中所示,但再次建议优先考虑与 asyncio 完全兼容的WriteOnlyCollection
。
动态关系策略允许配置一个 relationship()
,当在实例上访问时,将返回一个旧版的 Query
对象,而不是集合。然后可以进一步修改返回的 Query
对象,以便基于过滤条件迭代数据库集合。返回的 Query
对象是 AppenderQuery
的实例,它结合了 Query
的加载和迭代行为,以及 rudimentary 集合变异方法,如 AppenderQuery.append()
和 AppenderQuery.remove()
。
可以使用带有类型注释的 Declarative 形式配置“动态”加载器策略,使用 DynamicMapped
注解类:
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
启用了“自动刷新”,这将在每次集合即将发出查询时自动发生。
动态关系加载器 - API
对象名称 | 描述 |
AppenderQuery | 支持基本集合存储操作的动态查询。 |
DynamicMapped | 代表“动态”关系的 ORM 映射属性类型。 |
class sqlalchemy.orm.AppenderQuery
支持基本集合存储操作的动态查询。
AppenderQuery
上的方法包括 Query
的所有方法,以及用于集合持久化的附加方法。
成员
add(), add_all(), append(), count(), extend(), remove()
类签名
类 sqlalchemy.orm.AppenderQuery
(sqlalchemy.orm.dynamic.AppenderMixin
, sqlalchemy.orm.Query
)
method add(item: _T) → None
继承自 AppenderMixin.add()
方法的 AppenderMixin
将项目添加到此 AppenderQuery
。
给定的项目将在下一次 flush 时以父实例集合的形式持久化到数据库中。
此方法旨在帮助与 WriteOnlyCollection
集合类实现向前兼容。
版本 2.0 中的新功能。
method add_all(iterator: Iterable[_T]) → None
继承自 AppenderMixin.add_all()
方法的 AppenderMixin
将可迭代项目添加到此 AppenderQuery
。
给定的项目将在下一次 flush 时以父实例集合的形式持久化到数据库中。
此方法旨在帮助与 WriteOnlyCollection
集合类实现向前兼容。
版本 2.0 中的新功能。
method append(item: _T) → None
继承自 AppenderMixin.append()
方法的 AppenderMixin
将项目追加到此 AppenderQuery
。
给定的项目将在下一次 flush 时以父实例集合的形式持久化到数据库中。
method count() → int
继承自 AppenderMixin.count()
方法的 AppenderMixin
返回此 Query
形成的 SQL 返回的行数。
这将生成此查询的 SQL 如下:
SELECT count(1) AS count_1 FROM ( SELECT <rest of query follows...> ) AS anon_1
上述 SQL 返回一行,即计数函数的聚合值;然后 Query.count()
方法返回该单个整数值。
警告
需要注意的是,count() 返回的值与此 Query 从诸如 .all() 方法返回的 ORM 对象数量不同。当 Query
对象被要求返回完整实体时,将根据主键对条目进行去重,这意味着如果相同的主键值在结果中出现多次,则仅存在一个该主键的对象。这不适用于针对个别列的查询。
另请参阅
我的查询返回的对象数量与 query.count() 告诉我的不同 - 为什么?
若要对特定列进行精细化计数控制,跳过子查询的使用或以其他方式控制 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)))
另请参阅
2.0 迁移 - ORM 使用
method extend(iterator: Iterable[_T]) → None
继承自 AppenderMixin.extend()
方法的 AppenderMixin
将项目的可迭代对象添加到此AppenderQuery
中。
给定的项目将在下次 flush 时以父实例的集合的形式持久化到数据库中。
method remove(item: _T) → None
继承自 AppenderMixin.remove()
方法的 AppenderMixin
从此 AppenderQuery
中移除一个项目。
下次 flush 时,给定的项目将从父实例的集合中移除。
class sqlalchemy.orm.DynamicMapped
表示“动态”关系的 ORM 映射属性类型。
DynamicMapped
类型注释可以在 注释的声明性表 映射中使用,以指示应该为特定的 relationship()
使用 lazy="dynamic"
加载策略。
传统功能
“dynamic” 懒加载策略是现在称为“write_only”策略的传统形式,详情请参见 写入关系 部分。
例如:
class User(Base): __tablename__ = "user" id: Mapped[int] = mapped_column(primary_key=True) addresses: DynamicMapped[Address] = relationship( cascade="all,delete-orphan" )
请参阅 动态关系加载器 部分以了解背景知识。
2.0 版中新增。
另请参阅
动态关系加载器 - 完整背景
WriteOnlyMapped
- 完全 2.0 版本的风格
类签名
类 sqlalchemy.orm.DynamicMapped
(sqlalchemy.orm.base._MappedAnnotationBase
) ## 设置 RaiseLoad
当属性通常会发出懒加载时,“raise”-loaded 关系将引发一个InvalidRequestError
:
class MyClass(Base): __tablename__ = "some_table" # ... children: Mapped[List[MyRelatedClass]] = relationship(lazy="raise")
在上面的示例中,如果 children
集合之前未填充,则对该集合进行属性访问将引发异常。这包括读取访问,但对于集合还将影响写入访问,因为集合不能在未加载的情况下进行突变。这样做的原因是确保应用程序在某个特定上下文中不会发出任何意外的惰性加载。与必须通过 SQL 日志来确定所有必要属性是否已急切加载相比,“raise” 策略将在访问时立即引发未加载的属性。raise 策略也可基于查询选项使用 raiseload()
加载器选项。
另请参阅
使用 raiseload 防止不必要的惰性加载
使用被动删除
SQLAlchemy 中集合管理的一个重要方面是,当删除引用集合的对象时,SQLAlchemy 需要考虑到位于此集合内部的对象。这些对象将需要与父对象解除关联,对于一对多集合,这意味着外键列将被设置为 NULL,或者根据 级联 设置,可能希望为这些行发出 DELETE。
工作单元 过程只考虑逐行处理对象,这意味着 DELETE 操作意味着集合内的所有行必须在刷新过程中完全加载到内存中。对于大型集合来说,这是不可行的,因此我们转而依靠数据库自身的能力,使用外键 ON DELETE 规则自动更新或删除行,指示工作单元无需实际加载这些行即可处理它们。可以通过配置 relationship.passive_deletes
在 relationship()
构造上来指示工作单元以此方式工作;正在使用的外键约束也必须正确配置。
有关完整的“被动删除”配置的进一步细节,请参阅章节 使用 ORM 关系与外键 ON DELETE 级联。
只写关系
只写 加载器策略是配置 relationship()
的主要手段,它将保持可写,但不会加载其内容到内存中。现代类型注释的 Declarative 形式中的只写 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
集合中移除 AccountTransaction
对象时要执行的操作。这些元素是:
passive_deletes=True
- 允许工作单元在删除Account
时不必加载集合;请参阅使用 ORM 关系进行级联删除。ondelete="cascade"
配置在ForeignKey
约束上。这也在 使用 ORM 关系进行级联删除 中详细说明。cascade="all, delete-orphan"
- 指示工作单元在从集合中删除AccountTransaction
对象时删除它们。请参阅 delete-orphan 中的 Cascades 文档。
2.0 版本中的新功能:添加了“只写”关系加载器。
SqlAlchemy 2.0 中文文档(十三)(3)https://developer.aliyun.com/article/1562961