SqlAlchemy 2.0 中文文档(八十)(3)https://developer.aliyun.com/article/1559881
具有联接表继承的可变主键
在具有子表主键外键到父表主键的联接表继承配置上,现在可以在类似于 PostgreSQL 的具有级联功能的数据库上更新子表。mapper()
现在有一个选项 passive_updates=True
,表示此外键将自动更新。如果在不支持级联的数据库上,如 SQLite 或 MySQL/MyISAM,则将此标志设置为 False
。将来的功能增强将尝试根据正在使用的方言/表样式来自动配置此标志。
Beaker 缓存
Beaker 集成的一个有前途的新示例在 examples/beaker_caching
中。这是一个简单的示例,它在 Query
的结果生成引擎中应用了 Beaker 缓存。缓存参数通过 query.options()
提供,并允许完全控制缓存内容。SQLAlchemy 0.6 对 Session.merge()
方法进行了改进,以支持此类示例,并在大多数情况下提供了显著改进的性能。
其他更改
- 当选择多列/实体时,
Query
返回的“行元组”对象现在可以进行序列化,性能更高。 query.join()
已重新设计以提供更一致的行为和更灵活的功能(包括 [ticket:1537])query.select_from()
接受多个子句,以在 FROM 子句中生成多个逗号分隔的条目。在从多个 join() 子句中选择时非常有用。Session.merge()
上的“dont_load=True”标志已弃用,现在为“load=False”。- 添加了“make_transient()”助手函数,它将一个持久化/分离的实例转换为瞬态实例(即删除实例键并从任何会话中删除)。[ticket:1052]
- 在 mapper()上的 allow_null_pks 标志已弃用,并已更名为 allow_partial_pks。它默认为“on”。这意味着对于任何主键列具有非空值的行都将被视为标识。这种情况的需要通常仅在映射到外连接时发生。当设置为 False 时,具有 NULL 的 PK 将不被视为主键 - 特别是这意味着结果行将返回为 None(或不填入集合中),并且新的 0.6 版本还表示 session.merge()不会为此类 PK 值向数据库发出往返传输。【票号:1680】
- “backref”的机制已完全合并到更精细的“back_populates”系统中,并完全在
RelationProperty
的_generate_backref()
方法中进行。这使得RelationProperty
的初始化过程更简单,并允许更轻松地传播设置(例如从RelationProperty
的子类)。内部的BackRef()
已经消失,backref()
返回一个纯元组,RelationProperty
理解这个元组。 ResultProxy
的 keys 属性现在是一个方法,因此对它的引用(result.keys
)必须改为方法调用(result.keys()
)ResultProxy.last_inserted_ids
现已弃用,请改用ResultProxy.inserted_primary_key
。
已弃用/移除的 ORM 元素
大多数在 0.5 版本中已弃用并引发弃用警告的元素已移除(有几个例外)。所有标记为“待弃用”的元素现在已弃用,并将在使用时引发警告。
- ‘transactional’标志在 sessionmaker()和其他函数中已移除。使用‘autocommit=True’表示‘transactional=False’。
- 在 mapper()上的‘polymorphic_fetch’参数已移除。加载可以使用‘with_polymorphic’选项来控制。
- 在 mapper()上的‘select_table’参数已移除。为了实现此功能,请使用‘with_polymorphic=(“*”, )’。
- 在 synonym()上的‘proxy’参数已移除。此标志在 0.5 版本中未起作用,因为“proxy generation”行为现在是自动的。
- 对 joinedload()、joinedload_all()、contains_eager()、lazyload()、defer()和 undefer()传递单个元素列表而不是多个位置参数的做法已弃用。
- 对 query.order_by()、query.group_by()、query.join()或 query.outerjoin()传递单个元素列表而不是多个位置参数的做法已弃用。
query.iterate_instances()
已移除。使用query.instances()
。Query.query_from_parent()
已移除。使用 sqlalchemy.orm.with_parent()函数生成一个“parent”子句,或者使用query.with_parent()
。query._from_self()
已移除,请改用query.from_self()
。- 对 composite()的“comparator”参数已移除。使用“comparator_factory”。
RelationProperty._get_join()
已移除。- Session 上的‘echo_uow’标志已移除。在“sqlalchemy.orm.unitofwork”名称上使用日志记录。
session.clear()
已移除。请使用session.expunge_all()
。session.save()
、session.update()
和session.save_or_update()
已移除。请使用session.add()
和session.add_all()
。session.flush()
中的 “objects” 标志仍然被弃用。session.merge()
中的 “dont_load=True” 标志已弃用,改为使用 “load=False”。ScopedSession.mapper
仍然被弃用。请参阅www.sqlalchemy.org/trac/wiki/Usag
eRecipes/SessionAwareMapper 上的使用配方。- 将
InstanceState
(内部 SQLAlchemy 状态对象)传递给attributes.init_collection()
或attributes.get_history()
已被弃用。这些函数是公共 API,通常期望一个常规映射对象实例。 declarative_base()
中的 ‘engine’ 参数已被移除。请使用 ‘bind’ 关键字参数。
新的工作单元
工作单元的内部,主要是 topological.py
和 unitofwork.py
,已完全重写并大大简化。这对使用没有影响,因为所有现有的刷新行为都被完全保留了(或者至少在我们的测试套件和少数大量测试的生产环境中被保留了)。刷新(flush)的性能现在使用的方法调用减少了 20-30%,而且还应该使用更少的内存。源代码的意图和流程现在应该相当容易跟踪,并且刷新的架构在这一点上相当开放,为潜在的新技术领域提供了空间。刷新过程不再依赖于递归,因此可以刷新任意大小和复杂度的刷新计划。此外,映射器的“保存”过程,发出 INSERT 和 UPDATE 语句,现在缓存了这两个语句的“编译”形式,以便在非常大的刷新中进一步大幅减少调用次数。
请尽快向我们报告在刷新与 0.6 或 0.5 早期版本之间观察到的任何行为变化——我们将确保不会丢失任何功能。
对 query.update()
和 query.delete()
的更改
query.update()
中的‘expire’选项已更名为‘fetch’,与query.delete()
的命名一致。query.update()
和query.delete()
在同步策略上都默认为 ‘evaluate’。- 对
update()
和delete()
的 ‘synchronize’ 策略在失败时会引发错误。没有隐式回退到“fetch”。评估的失败是基于条件结构的,因此基于代码结构,成功/失败是可以确定的。
relation()
正式更名为 relationship()
这是为了解决“relation”在关系代数中表示“表或派生表”的长期问题。relation()
这个名字,打字更少,将在可预见的将来继续存在,所以这个变化应该完全没有痛苦。
子查询的贪婪加载
添加了一种名为“subquery”加载的新类型的急切加载。这是一种加载,它在第一个加载完整集合的 SQL 查询之后立即发出第二个 SQL 查询,通过 INNER JOIN 连接到第一个查询中的所有父级。子查询加载类似于当前的连接预加载,使用subqueryload()
、subqueryload_all()
选项,以及设置在relationship()
上的lazy='subquery'
。子查询加载通常更有效地加载许多较大的集合,因为它无条件地使用 INNER JOIN,而且也不会重新加载父行。
eagerload()
, eagerload_all()
现在是joinedload()
, joinedload_all()
为了为新的子查询加载功能腾出空间,现有的eagerload()
/eagerload_all()
options are now superseded by joinedload()
and joinedload_all()
. The old names will hang around for the foreseeable future just like relation()
。
lazy=False|None|True|'dynamic'
现在接受lazy='noload'|'joined'|'subquery'|'select'|'dynamic'
在加载器策略开放的主题上继续,relationship()
上的标准关键字lazy
选项现在是,用于延迟加载的select
(通过属性访问时发出的 SELECT),用于急切连接加载的joined
,用于急切子查询加载的subquery
,不应出现任何负载的noload
,以及用于“动态”关系的dynamic
。旧的True
, False
, None
参数仍然被接受,行为与以前完全相同。
在关系、joinedload 上设置 innerjoin=True
现在可以指示使用 INNER JOIN 而不是 OUTER JOIN 来连接预加载的标量和集合。在 PostgreSQL 上,这被观察到可以为某些查询提供 300-600% 的速度提升。为任何在 NOT NULLable 外键上的多对一设置此标志,以及对于任何保证存在相关项目的集合。
在映射器级别:
mapper(Child, child) mapper( Parent, parent, properties={"child": relationship(Child, lazy="joined", innerjoin=True)}, )
在查询时间级别:
session.query(Parent).options(joinedload(Parent.child, innerjoin=True)).all()
在 relationship()
级别的 innerjoin=True
标志也将对任何不覆盖该值的 joinedload()
选项产生影响。
对许多对一的增强
- 许多对一关系现在在更少的情况下会触发延迟加载,包括在大多数情况下不会在替换新值时获取“旧”值。
- 对于连接表子类的多对一关系现在使用 get() 进行简单加载(称为“use_get”条件),即
Related
->Sub(Base)
, 无需重新定义基表的主连接条件。[ticket:1186] - 指定具有声明列的外键,即
ForeignKey(MyRelatedClass.id)
不会阻止“use_get”条件的发生 [ticket:1492] - relationship()、joinedload() 和 joinedload_all() 现在具有一个名为“innerjoin”的选项。指定
True
或False
来控制是否构建一个 INNER 或 OUTER 连接的急切连接。默认始终为False
。映射器选项将覆盖在 relationship() 上指定的任何设置。通常应为多对一、非空外键关系设置以允许改进的连接性能。[ticket:1544] - 联接急切加载的行为,即当 LIMIT/OFFSET 存在时,主查询被包装在子查询中,现在对所有急切加载都是多对一联接的情况做了一个例外。在这些情况下,急切连接直接针对父表进行,同时限制/偏移量没有额外的子查询开销,因为多对一连接不会向结果添加行。
例如,在 0.5 版本中,这个查询:
session.query(Address).options(eagerload(Address.user)).limit(10)
- 会生成类似于以下的 SQL:
SELECT * FROM (SELECT * FROM addresses LIMIT 10) AS anon_1 LEFT OUTER JOIN users AS users_1 ON users_1.id = anon_1.addresses_user_id
- 这是因为任何急切加载器的存在都表明它们中的一些或全部可能与多行集合相关联,这将需要将任何种类的行计数敏感修饰符(如 LIMIT)包装在子查询中。
在 0.6 版本中,该逻辑更加敏感,可以检测到所有急切加载器是否代表多对一关系,如果是这种情况,则急切连接不会影响行数:
SELECT * FROM addresses LEFT OUTER JOIN users AS users_1 ON users_1.id = addresses.user_id LIMIT 10
使用联接表继承的可变主键
在子表具有外键到父表主键的联接表继承配置中,现在可以在类似 PostgreSQL 这样支持级联的数据库上进行更新。mapper()
现在有一个选项passive_updates=True
,表示此外键将自动更新。如果在不支持级联的数据库上,如 SQLite 或 MySQL/MyISAM 上,将此标志设置为False
。未来的功能增强将尝试根据使用的方言/表样式自动配置此标志。
Beaker 缓存
Beaker 集成的一个有前途的新例子在examples/beaker_caching
中。这是一个简单的配方,将 Beaker 缓存应用于Query
的结果生成引擎中。缓存参数通过query.options()
提供,并允许完全控制缓存的内容。SQLAlchemy 0.6 对Session.merge()
方法进行了改进,以支持这种和类似的配方,并在大多数情况下提供了显著改进的性能。
其他变化
- 当选择多列/实体时,
Query
返回的“行元组”对象现在也是可序列化的,并且性能更高。 query.join()
已经重新设计,以提供更一致的行为和更灵活性(包括[ticket:1537])query.select_from()
接受多个子句,以在 FROM 子句中产生多个逗号分隔的条目。在从多个 join()子句中选择时很有用。Session.merge()
上的“dont_load=True”标志已被弃用,现在是“load=False”。- 添加了“make_transient()”辅助函数,将持久/分离实例转换为瞬态实例(即删除实例键并从任何会话中移除。)[ticket:1052]
mapper()
上的allow_null_pks
标志已被弃用,并已重命名为allow_partial_pks
。默认情况下已打开。这意味着对于任何主键列中有非空值的行将被视为标识。这种情况通常只在映射到外连接时发生。当设置为 False 时,具有 NULL 值的 PK 将不被视为主键 - 特别是这意味着结果行将返回为 None(或不会填充到集合中),并且在 0.6 版本中还表示session.merge()
不会为此类 PK 值发出往返数据库的请求。[ticket:1680]- “backref”的机制已完全合并到更精细的“back_populates”系统中,并完全在
RelationProperty
的_generate_backref()
方法中进行。这使得RelationProperty
的初始化过程更简单,并允许更容易地传播设置(例如从RelationProperty
的子类)到反向引用。内部的BackRef()
已经消失,backref()
返回一个被RelationProperty
理解的普通元组。 ResultProxy
的keys
属性现在是一个方法,因此对它的引用(result.keys
)必须更改为方法调用(result.keys()
)。ResultProxy.last_inserted_ids
现已弃用,改用ResultProxy.inserted_primary_key
。
弃用/移除的 ORM 元素
在 0.5 版本中被弃用并引发弃用警告的大多数元素已被移除(有少数例外)。所有标记为“即将弃用”的元素现在已被弃用,并在使用时会引发警告。
sessionmaker()
和其他地方的‘transactional’标志已被移除。使用‘autocommit=True’来表示‘transactional=False’。mapper()
上的‘polymorphic_fetch’参数已被移除。可以使用‘with_polymorphic’选项来控制加载。mapper()
上的‘select_table’参数已被移除。使用‘with_polymorphic=(“*”, )’来实现此功能。synonym()
上的‘proxy’参数已被移除。在 0.5 版本中,此标志没有任何作用,因为“代理生成”行为现在是自动的。- 将元素的单个列表传递给
joinedload()
、joinedload_all()
、contains_eager()
、lazyload()
、defer()
和undefer()
,而不是多个位置*args,已被弃用。 - 将元素的单个列表传递给
query.order_by()
、query.group_by()
、query.join()
或query.outerjoin()
,而不是多个位置*args,已被弃用。 - 移除了
query.iterate_instances()
。使用query.instances()
。 - 移除了
Query.query_from_parent()
。使用sqlalchemy.orm.with_parent()
函数生成“parent”子句,或者使用query.with_parent()
。 - 移除了
query._from_self()
,请改用query.from_self()
。 composite()
的“comparator”参数已被移除。使用“comparator_factory”。- 移除了
RelationProperty._get_join()
。 Session
上的‘echo_uow’标志已被移除。在“sqlalchemy.orm.unitofwork”名称上使用日志记录。session.clear()
被移除。使用session.expunge_all()
。session.save()
、session.update()
、session.save_or_update()
被移除。使用session.add()
和session.add_all()
。- 在
session.flush()
上的 “objects” 标志仍然被弃用。 - 在
session.merge()
上的 “dont_load=True” 标志已被弃用,改用 “load=False”。 ScopedSession.mapper
仍然被弃用。参见使用方法的示例:www.sqlalchemy.org/trac/wiki/Usag
eRecipes/SessionAwareMapper- 在
attributes.init_collection()
或attributes.get_history()
中传递InstanceState
(内部 SQLAlchemy 状态对象)已被弃用。这些函数是公共 API,通常期望普通映射对象实例。 declarative_base()
上的 ‘engine’ 参数已被移除。使用 ‘bind’ 关键字参数。
扩展
SQLSoup
SQLSoup 已经现代化并更新以反映常见的 0.5/0.6 功能,包括明确定义的会话集成。请阅读新文档:[www.sqlalc
hemy.org/docs/06/reference/ext/sqlsoup.html]。
Declarative
DeclarativeMeta
(declarative_base
的默认元类)以前允许子类修改 dict_
来添加类属性(例如列)。这种方式已不再起作用,DeclarativeMeta
构造函数现在忽略 dict_
。相反,类属性应直接赋值,例如 cls.id=Column(...)
,或者应该使用 MixIn 类 方法而不是元类方法。
SQLSoup
SQLSoup 已经现代化并更新以反映常见的 0.5/0.6 功能,包括明确定义的会话集成。请阅读新文档:[www.sqlalc
hemy.org/docs/06/reference/ext/sqlsoup.html]。
Declarative
DeclarativeMeta
(declarative_base
的默认元类)以前允许子类修改 dict_
来添加类属性(例如列)。这种方式已不再起作用,DeclarativeMeta
构造函数现在忽略 dict_
。相反,类属性应直接赋值,例如 cls.id=Column(...)
,或者应该使用 MixIn 类 方法而不是元类方法。
SQLAlchemy 0.5 中有什么新功能?
关于本文档
本文档描述了 SQLAlchemy 版本 0.4(最后发布于 2008 年 10 月 12 日)与 SQLAlchemy 版本 0.5(最后发布于 2010 年 1 月 16 日)之间的变化。
文档日期:2009 年 8 月 4 日
本指南记录了影响用户从 SQLAlchemy 0.4 系列迁移到 0.5 系列的 API 更改。对于那些从Essential SQLAlchemy开始工作的人也是推荐的,该书只涵盖了 0.4 版本,甚至在其中有一些旧的 0.3 版本的内容。请注意,SQLAlchemy 0.5 删除了在整个 0.4 系列中已弃用的许多行为,并且还弃用了更多与 0.4 特定的行为。
主要文档更改
文档的一些部分已经完全重写,可以作为新 ORM 功能的介绍。特别是Query
和Session
对象在 API 和行为上有一些明显的区别,这些区别从根本上改变了许多基本操作的方式,特别是构建高度定制的 ORM 查询和处理过时的会话状态、提交和回滚。
弃用来源
另一个信息源记录在一系列单元测试中,展示了一些常见Query
模式的最新用法;此文件可在[source:sqlalchemy/trunk/test/orm/test_deprecations.py]中查看。
要求更改
- 需要 Python 2.4 或更高版本。SQLAlchemy 0.4 系列是最后一个支持 Python 2.3 的版本。
对象关系映射
- Query 中的列级表达式。 - 如教程中所述,
Query
具有创建特定 SELECT 语句的能力,而不仅仅是针对完整行的语句:
session.query(User.name, func.count(Address.id).label("numaddresses")).join( Address ).group_by(User.name)
- 任何多列/实体查询返回的元组都是命名元组:
for row in ( session.query(User.name, func.count(Address.id).label("numaddresses")) .join(Address) .group_by(User.name) ): print("name", row.name, "number", row.numaddresses)
Query
具有statement
访问器,以及一个subquery()
方法,允许Query
用于创建更复杂的组合:
subq = ( session.query(Keyword.id.label("keyword_id")) .filter(Keyword.name.in_(["beans", "carrots"])) .subquery() ) recipes = session.query(Recipe).filter( exists() .where(Recipe.id == recipe_keywords.c.recipe_id) .where(recipe_keywords.c.keyword_id == subq.c.keyword_id) )
- 建议使用显式 ORM 别名进行别名连接 -
aliased()
函数生成一个类的“别名”,允许在 ORM 查询中与别名进行细粒度控制。虽然仍然可以使用表级别的别名(即table.alias()
),但 ORM 级别的别名保留了 ORM 映射对象的语义,这对于继承映射、选项和其他场景非常重要。例如:
Friend = aliased(Person) session.query(Person, Friend).join((Friend, Person.friends)).all()
- query.join()功能大大增强。 - 您现在可以通过多种方式指定连接的目标和 ON 子句。可以仅提供目标类,SQLA 将尝试通过相同的外键形式连接到它,就像
table.join(someothertable)
一样。还可以提供目标和显式的 ON 条件,其中 ON 条件可以是relation()
名称,实际类描述符或 SQL 表达式。或者也可以像以前那样只提供relation()
名称或类描述符。请参阅 ORM 教程,其中有几个示例。 - 建议使用声明性用于不需要(且不喜欢)表和映射器之间抽象的应用程序 - [/docs/05/reference/ext/declarative.html 声明性]模块用于将
Table
、mapper()
和用户定义的类对象的表达结合在一起,强烈建议使用它,因为它简化了应用程序配置,确保了“每个类一个映射器”的模式,并允许对不同的mapper()
调用提供完整的配置范围。将mapper()
和Table
的使用分开现在被称为“经典 SQLAlchemy 使用方式”,当然可以与声明性混合使用。 - 已从类中删除了
.c.
属性(即MyClass.c.somecolumn
)。与 0.4 版本一样,类级别的属性可用作查询元素,即Class.c.propname
现在被Class.propname
所取代,并且c
属性仍然保留在Table
对象上,其中它们指示存在于表上的Column
对象的命名空间。
要获取映射类的表(如果您之前没有保留它):
table = class_mapper(someclass).mapped_table
- 迭代遍历列:
for col in table.c: print(col)
- 使用特定列进行操作:
table.c.somecolumn
- 类绑定描述符支持完整的 Column 运算符集,以及文档化的与关系有关的运算符,如
has()
、any()
、contains()
等。
删除.c.
的原因是,在 0.5 版本中,类绑定描述符可能具有不同的含义,以及关于类映射的信息,与普通的Column
对象不同-并且存在一些情况,您会特别想要使用其中之一。通常,使用类绑定描述符会调用一组映射/多态感知的转换,而使用表绑定列则不会。在 0.4 版本中,这些转换适用于所有表达式,但是 0.5 版本完全区分列和映射描述符,仅将转换应用于后者。因此,在许多情况下,特别是在处理连接的表继承配置以及使用query()
时,Class.propname
和table.c.colname
不可互换。
例如,session.query(users.c.id, users.c.name)
与session.query(User.id, User.name)
是不同的;在后一种情况下,Query
知道正在使用的映射器,并且可以使用进一步的映射器特定操作,如query.join()
,query.with_parent()
等,但在前一种情况下不行。此外,在多态继承场景中,类绑定描述符指的是多态可选择使用的列,而不一定是直接对应描述符的表列。例如,一组类通过连接表继承与person
表相关联,每个表的person_id
列都将其Class.person_id
属性映射到person
中的person_id
列,而不是其子类表。版本 0.4 会自动将此行为映射到表绑定的Column
对象上。在 0.5 中,已移除了此自动转换,因此实际上可以使用表绑定列来覆盖多态查询时发生的转换;这使得Query
能够在连接表或具体表继承设置中创建优化的选择,以及可移植的子查询等。 - 会话现在与事务自动同步。 会话现在默认情况下自动与事务同步,包括自动刷新和自动过期。除非使用
autocommit
选项禁用,否则始终存在事务。当所有三个标志都设置为默认值时,会话在回滚后能够优雅地恢复,并且很难将过时数据导入会话中。详细信息请参阅新的会话文档。 - 隐式排序已移除。这将影响依赖于 SA 的“隐式排序”行为的 ORM 用户,该行为规定所有没有
order_by()
的 Query 对象将按照主映射表的“id”或“oid”列进行排序,并且所有延迟/急切加载的集合都应用类似的排序。在 0.5 中,必须显式配置mapper()
和relation()
对象上的自动排序(如果需要),或者在使用Query
时。
要将 0.4 映射转换为 0.5,使其排序行为与 0.4 或之前的版本极为相似,请在mapper()
和relation()
上使用order_by
设置:
mapper( User, users, properties={"addresses": relation(Address, order_by=addresses.c.id)}, order_by=users.c.id, )
- 要在 backref 上设置排序,请使用
backref()
函数:
"keywords": relation( Keyword, secondary=item_keywords, order_by=keywords.c.name, backref=backref("items", order_by=items.c.id), )
- 使用声明式?为了帮助满足新的
order_by
要求,现在可以使用稍后在 Python 中评估的字符串来设置order_by
和相关内容(这仅适用于声明式,而不是普通的映射器):
class MyClass(MyDeclarativeBase): ... "addresses": relation("Address", order_by="Address.id")
- 通常在加载基于列表的项目集合的
relation()
上设置order_by
是一个好主意,因为否则无法影响排序。除此之外,最佳实践是使用Query.order_by()
来控制加载的主要实体的排序。 - Session 现在是 autoflush=True/autoexpire=True/autocommit=False。 - 要设置它,只需调用
sessionmaker()
而不带任何参数。现在transactional=True
的名称是autocommit=False
。刷新发生在每次查询时(可通过autoflush=False
禁用),在每次commit()
之前(一如既往),以及在每次begin_nested()
之前(因此回滚到 SAVEPOINT 是有意义的)。所有对象在每次commit()
和每次rollback()
后都会过期。回滚后,待定对象被清除,删除的对象移回持久状态。这些默认设置非常好地协同工作,实际上不再需要像clear()
这样的旧技术(也已重命名为expunge_all()
)。
P.S.: 在rollback()
后,会话现在是可重用的。标量和集合属性的更改、添加和删除都会被回滚。 - session.add()取代了 session.save()、session.update()、session.save_or_update()。 -
session.add(someitem)
和session.add_all([list of items])
方法取代了save()
、update()
和save_or_update()
。这些方法将在整个 0.5 版本中继续被弃用。 - backref 配置更简洁。 -
backref()
函数现在在未明确声明时使用前向relation()
的primaryjoin
和secondaryjoin
参数。在两个方向上分别指定primaryjoin
/secondaryjoin
不再必要。 - 简化的多态选项。 - ORM 的“多态加载”行为已经简化。在 0.4 版本中,mapper()有一个名为
polymorphic_fetch
的参数,可以配置为select
或deferred
。此选项已被移除;现在映射器将仅推迟未包含在 SELECT 语句中的任何列。实际使用的 SELECT 语句由with_polymorphic
映射器参数控制(在 0.4 中也有,替代了select_table
),以及Query
上的with_polymorphic()
方法(同样在 0.4 中)。
对继承类的延迟加载进行了改进,现在映射器在所有情况下都会生成“优化”版本的 SELECT 语句;也就是说,如果类 B 继承自 A,并且类 B 上的几个属性已过期,刷新操作将只包括 B 的表在 SELECT 语句中,不会 JOIN 到 A。 Session
上的execute()
方法将普通字符串转换为text()
构造,以便所有绑定参数都可以指定为“:bindname”而无需显式调用text()
。如果需要“原始”SQL,请使用session.connection().execute("raw text")
。session.Query().iterate_instances()
已重命名为instances()
。旧的返回列表而不是迭代器的instances()
方法已不复存在。如果你依赖于该行为,应该使用list(your_query.instances())
。
扩展 ORM
在 0.5 版本中,我们将继续提供更多修改和扩展 ORM 的方法。以下是摘要:
- MapperExtension. - 这是经典的扩展类,仍然存在。很少需要的方法是
create_instance()
和populate_instance()
。要控制从数据库加载对象时的初始化,使用reconstruct_instance()
方法,或者更容易地使用文档中描述的@reconstructor
装饰器。 - SessionExtension. - 这是一个易于使用的会话事件扩展类。特别是,它提供了
before_flush()
、after_flush()
和after_flush_postexec()
方法。在许多情况下,推荐使用这种用法,而不是MapperExtension.before_XXX
,因为在before_flush()
中,您可以自由修改会话的刷新计划,这是无法从MapperExtension
中完成的。 - AttributeExtension. - 这个类现在是公共 API 的一部分,允许拦截属性上的用户事件,包括属性设置和删除操作,以及集合追加和删除。它还允许修改要设置或追加的值。文档中描述的
@validates
装饰器提供了一种快速的方式,将任何映射属性标记为特定类方法“验证”。 - Attribute Instrumentation Customization. - 提供了一个 API,用于雄心勃勃地完全替换 SQLAlchemy 的属性检测,或者仅在某些情况下进行增强。这个 API 是为 Trellis 工具包而制作的,但作为公共 API 可用。在分发的
/examples/custom_attributes
目录中提供了一些示例。
模式/类型
- String with no length no longer generates TEXT, it generates VARCHAR - 当未指定长度时,
String
类型不再神奇地转换为Text
类型。这只在发出 CREATE TABLE 时才会生效,因为它将发出没有长度参数的VARCHAR
,这在许多(但不是所有)数据库上是无效的。要创建 TEXT(或 CLOB,即无界字符串)列,请使用Text
类型。 - PickleType() with mutable=True requires an eq() method - 当
PickleType
类型的mutable=True
时,需要比较值。比较pickle.dumps()
的方法效率低下且不可靠。如果传入对象没有实现__eq__()
,并且也不是None
,则使用dumps()
进行比较,但会发出警告。对于实现__eq__()
的类型,包括所有字典、列表等,比较将使用==
,默认情况下是可靠的。 - TypeEngine/TypeDecorator 的
convert_bind_param()
和convert_result_value()
方法已移除。 - 不幸的是,O’Reilly 书籍在 0.3 之后弃用了这些方法,但仍然对其进行了文档记录。对于一个子类化TypeEngine
的用户定义类型,应该使用bind_processor()
和result_processor()
方法进行绑定/结果处理。任何用户定义类型,无论是扩展TypeEngine
还是TypeDecorator
,只要使用旧的 0.3 风格,都可以通过以下适配器轻松地调整为新风格:
class AdaptOldConvertMethods(object): """A mixin which adapts 0.3-style convert_bind_param and convert_result_value methods """ def bind_processor(self, dialect): def convert(value): return self.convert_bind_param(value, dialect) return convert def result_processor(self, dialect): def convert(value): return self.convert_result_value(value, dialect) return convert def convert_result_value(self, value, dialect): return value def convert_bind_param(self, value, dialect): return value
- 要使用上述混合项:
class MyType(AdaptOldConvertMethods, TypeEngine): ...
Column
和Table
上的quote
标志以及Table
上的quote_schema
标志现在控制引用方式,包括正面和负面。默认值为None
,表示让常规的引用规则生效。当为True
时,强制引用。当为False
时,强制不引用。- 现在可以更方便地使用
Column(..., server_default='val')
指定列DEFAULT
值的 DDL,废弃了Column(..., PassiveDefault('val'))
。default=
现在仅用于 Python 初始化的默认值,并且可以与server_default
共存。新的server_default=FetchedValue()
取代了标记列受外部触发器影响的PassiveDefault('')
习惯用法,没有 DDL 的副作用。 - SQLite 的
DateTime
、Time
和Date
类型现在仅接受 datetime 对象,而不接受字符串作为绑定参数输入。如果想要创建自己的“混合”类型,它接受字符串并将结果返回为日期对象(可以是任何格式),则创建一个基于String
的TypeDecorator
。如果只想要基于字符串的日期,只需使用String
。 - 此外,当与 SQLite 一起使用时,
DateTime
和Time
类型现在以与str(datetime)
相同的方式表示 Pythondatetime.datetime
对象的 “微秒” 字段,即作为小数秒,而不是微秒的计数。也就是说:
dt = datetime.datetime(2008, 6, 27, 12, 0, 0, 125) # 125 usec # old way "2008-06-27 12:00:00.125" # new way "2008-06-27 12:00:00.000125"
- 因此,如果现有的基于文件的 SQLite 数据库打算在 0.4 和 0.5 之间使用,您必须将 datetime 列升级为存储新格式(注意:请测试此功能,我相信它是正确的):
UPDATE mytable SET somedatecol = substr(somedatecol, 0, 19) || '.' || substr((substr(somedatecol, 21, -1) / 1000000), 3, -1);
- 或者,可以按以下方式启用“传统”模式:
from sqlalchemy.databases.sqlite import DateTimeMixin DateTimeMixin.__legacy_microseconds__ = True
SqlAlchemy 2.0 中文文档(八十)(5)https://developer.aliyun.com/article/1559885