SqlAlchemy 2.0 中文文档(七十九)(5)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: SqlAlchemy 2.0 中文文档(七十九)

SqlAlchemy 2.0 中文文档(七十九)(4)https://developer.aliyun.com/article/1561107


Session.merge()检查带版本的映射器的版本 ID

Session.merge()将检查传入状态的版本 ID 与数据库的版本 ID 是否匹配,假设映射使用版本 ID 并且传入状态已分配版本 ID,并且如果它们不匹配,则引发 StaleDataError。这是正确的行为,即如果传入状态包含过时的版本 ID,则应假定状态是过时的。

如果将数据合并到带版本的状态中,则可以将版本 ID 属性留空,不会进行版本检查。

通过检查 Hibernate 的操作确认了这一变化 - merge()和版本控制功能最初都是从 Hibernate 中适应过来的。

#2027

查询改进中的元组标签名称

这种改进对于依赖旧行为的应用程序可能会有一点点向后不兼容。

给定两个映射类FooBar,每个类都有一个名为spam的列:

qa = session.query(Foo.spam)
qb = session.query(Bar.spam)
qu = qa.union(qb)

qu生成的单列的名称将是spam。以前会是类似于foo_spam的东西,这是由于union的组合方式导致的,这与非联合查询中的名称spam不一致。

#1942

映射列属性首先引用最具体的列

这是在映射列属性引用多个列时涉及的行为变化,特别是在处理具有与超类属性相同名称的连接表子类上的属性时。

使用声明性,情景如下:

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
class Child(Parent):
    __tablename__ = "child"
    id = Column(Integer, ForeignKey("parent.id"), primary_key=True)

上面,属性Child.id同时指代child.id列和parent.id - 这是由于属性的名称。如果在类上命名不同,比如Child.child_id,那么它将明确映射到child.id,而Child.id将成为与Parent.id相同的属性。

id属性被设置为引用parent.idchild.id时,它们被存储在一个有序列表中。这样,诸如Child.id的表达式在呈现时只会引用其中的一个列。直到 0.6 版本,这一列将是parent.id。在 0.7 版本中,它是更少令人惊讶的child.id

这种行为的遗留涉及到 ORM 的行为和限制,这些限制实际上已经不再适用;只需要改变顺序即可。

这种方法的一个主要优势是现在更容易构建引用本地列的primaryjoin表达式:

class Child(Parent):
    __tablename__ = "child"
    id = Column(Integer, ForeignKey("parent.id"), primary_key=True)
    some_related = relationship(
        "SomeRelated", primaryjoin="Child.id==SomeRelated.child_id"
    )
class SomeRelated(Base):
    __tablename__ = "some_related"
    id = Column(Integer, primary_key=True)
    child_id = Column(Integer, ForeignKey("child.id"))

在 0.7 之前,Child.id表达式将引用Parent.id,并且需要将child.id映射到一个不同的属性。

这也意味着这样一个查询的行为发生了变化:

session.query(Parent).filter(Child.id > 7)

在 0.6 版本中,会呈现如下:

SELECT  parent.id  AS  parent_id
FROM  parent
WHERE  parent.id  >  :id_1

在 0.7 中,你会得到:

SELECT  parent.id  AS  parent_id
FROM  parent,  child
WHERE  child.id  >  :id_1

你会注意到这是一个笛卡尔积 - 这种行为现在等同于任何本地于Child的其他属性。with_polymorphic()方法,或者类似的显式连接基础Table对象的策略,用于对所有具有针对Child的条件的Parent对象进行查询,方式与 0.5 和 0.6 相同:

print(s.query(Parent).with_polymorphic([Child]).filter(Child.id > 7))

在 0.6 和 0.7 版本中都会呈现:

SELECT  parent.id  AS  parent_id,  child.id  AS  child_id
FROM  parent  LEFT  OUTER  JOIN  child  ON  parent.id  =  child.id
WHERE  child.id  >  :id_1

这种变化的另一个影响是,跨两个表的连接继承加载将从子表的值填充,而不是父表的值。一个不寻常的情况是,使用with_polymorphic="*"对“Parent”进行查询会发出一个针对“parent”的查询,并与“child”进行左外连接。行位于“Parent”,看到多态标识对应于“Child”,但假设“child”中的实际行已经删除。由于这种损坏,行中所有与“child”对应的列都设置为 NULL - 这现在是被填充的值,而不是父表中的值。

#1892

映射到具有两个或更多同名列的连接需要明确声明

这与#1892中的先前更改有些相关。在映射到连接时,同名列必须明确链接到映射属性,即如映射一个类到多个表中所述。

给定两个表foobar,每个表都有一个主键列id,现在执行以下操作会产生错误:

foobar = foo.join(bar, foo.c.id == bar.c.foo_id)
mapper(FooBar, foobar)

这是因为mapper()拒绝猜测哪一列是FooBar.id的主要表示 - 是foo.c.id还是bar.c.id?属性必须明确:

foobar = foo.join(bar, foo.c.id == bar.c.foo_id)
mapper(FooBar, foobar, properties={"id": [foo.c.id, bar.c.id]})

#1896

映射器要求在映射的可选择项中存在多态列

这在 0.6 中是一个警告,现在在 0.7 中是一个错误。为polymorphic_on指定的列必须在映射的可选择项中。这是为了防止一些偶尔发生的用户错误,例如:

mapper(SomeClass, sometable, polymorphic_on=some_lookup_table.c.id)

在上面的例子中,polymorphic_on需要在sometable列上,此时可能是sometable.c.some_lookup_id。有时也会发生一些类似的“多态联合”场景中的错误。

这种配置错误一直是“错误的”,上述映射不按规定工作 - 列将被忽略。然而,在极少数情况下,如果应用程序无意中依赖于这种行为,则可能会产生潜在的向后不兼容性。

#1875

DDL()构造现在转义百分号

以前,在DDL()字符串中的百分号必须进行转义,即根据 DBAPI,对于那些接受pyformatformat绑定(例如 psycopg2,mysql-python)的 DBAPI,这是不一致的,与text()构造自动执行的操作不同。现在,DDL()text()一样进行相同的转义。

#1897

Table.c / MetaData.tables稍作调整,不允许直接变异

另一个领域,一些用户在进行尝试时,并不按预期工作,但仍然存在极小的可能性,即某些应用程序依赖于这种行为,Table.c属性和MetaData.tables属性返回的构造明确是不可变的。构造的“可变”版本现在是私有的。向.c添加列涉及使用Tableappend_column()方法,这确保了事物以适当的方式与父Table关联;同样,MetaData.tables与存储在此字典中的Table对象有合同,还有一点新的簿记,即跟踪所有模式名称的set(),只有使用公共Table构造函数以及Table.tometadata()才能满足。

当然,ColumnCollectiondict 集合在这些属性上进行咨询的情况下,可能会有一天实现所有变异方法的事件,从而在直接变异集合时发生适当的簿记,但在有人有动力实现所有这些以及数十个新单元测试之前,缩小这些集合的变异路径将确保没有应用程序试图依赖当前不受支持的用法。

#1893 #1917

server_default 对所有 inserted_primary_key 值一致地返回 None。

当在 Integer PK 列上存在 server_default 时,已确立一致性。 SQLA 不会预取这些值,它们也不会在  cursor.lastrowid(DBAPI)中返回。 确保所有后端一致地在 result.inserted_primary_key  中为这些值返回 None-一些后端以前可能返回了一个值。 在主键列上使用 server_default 是极不寻常的。 如果使用特殊函数或  SQL 表达式生成主键默认值,则应将其建立为 Python 端的“默认”而不是 server_default。

关于此情况的反射,具有服务器默认值的 int PK 列的反射将 “autoincrement” 标志设置为 False,但在检测到 PG SERIAL 列的序列默认值的情况下除外。

#2020 #2021

sqlalchemy.exceptions 在 sys.modules 中的别名已被移除。

几年来,我们已经将字符串 sqlalchemy.exceptions 添加到 sys.modules 中,以便像 “import sqlalchemy.exceptions” 这样的语句可以工作。 核心异常模块的名称已经很久以来是 exc,因此建议导入此模块的方式是:

from sqlalchemy import 

exceptions 名称仍然存在于“sqlalchemy”中,供可能已经使用 from sqlalchemy import exceptions 的应用程序使用,但他们也应该开始使用 exc 名称。

查询时间配方更改

虽然不是 SQLAlchemy 本身的一部分,但值得一提的是,将 ConnectionProxy 重构为新的事件系统意味着它不再适用于“Timing all Queries”配方。 请调整查询计时器以使用 before_cursor_execute()after_cursor_execute() 事件,在更新的配方 UsageRecipes/Profiling 中有示例。

已弃用的 API

类型的默认构造函数不会接受参数。

核心类型模块中的诸如 IntegerDate 等简单类型不接受参数。 从 0.7b4/0.7.0 开始,接受/忽略 catchall \*args, \**kwargs 的默认构造函数已经恢复,但会发出弃用警告。

如果正在使用诸如 Integer 等核心类型的参数,可能是您打算使用特定于方言的类型,例如 sqlalchemy.dialects.mysql.INTEGER,该类型接受“display_width”参数,例如。

compile_mappers()重命名为configure_mappers(),简化配置内部。

这个系统从一个小型、局部实现在单个映射器上,并且命名不当的东西慢慢演变成了更像是全局“注册表”级别函数且命名不当的东西,所以我们通过将实现移出Mapper并将其重命名为configure_mappers()来修复了这两个问题。当然,一般情况下应用程序通常不需要调用configure_mappers(),因为这个过程是根据需要的,一旦通过属性或查询访问需要映射时就会发生。

#1966

核心监听器/代理被事件监听器取代。

PoolListenerConnectionProxyDDLElement.execute_atevent.listen()取代,分别使用PoolEventsEngineEventsDDLEvents作为分派目标。

ORM 扩展被事件监听器取代。

MapperExtensionAttributeExtensionSessionExtensionevent.listen()取代,分别使用MapperEvents/InstanceEventsAttributeEventsSessionEvents作为分派目标。

在 MySQL 中,将字符串发送给 select()distinct 应通过前缀进行。

这个晦涩的功能允许使用 MySQL 后端的这种模式:

select([mytable], distinct="ALL", prefixes=["HIGH_PRIORITY"])

对于非标准或不寻常的前缀,应使用prefixes关键字或prefix_with()方法:

select([mytable]).prefix_with("HIGH_PRIORITY", "ALL")

useexistingextend_existingkeep_existing取代。

对表的useexisting标志已被新的一对标志keep_existingextend_existing取代。extend_existing等同于useexisting - 返回现有表,并添加额外的构造元素。使用keep_existing,返回现有表,但不添加额外的构造元素 - 这些元素仅在表新建时应用。

类的默认构造函数不接受参数。

核心类型模块中的简单类型,如IntegerDate等,不接受参数。接受/忽略通用参数 \*args, \**kwargs 的默认构造函数在 0.7b4/0.7.0 版本中已恢复,但会发出弃用警告。

如果在核心类型如Integer中使用参数,可能是您打算使用特定于方言的类型,例如sqlalchemy.dialects.mysql.INTEGER,例如它接受“display_width”参数。

compile_mappers()重命名为configure_mappers(),简化配置内部。

这个系统从一个小型、局部实现在单个映射器上,并且命名不当的东西慢慢演变成了更像是全局“注册表”级别函数且命名不当的东西,所以我们通过将实现移出Mapper并将其重命名为configure_mappers()来修复了这两个问题。当然,一般情况下应用程序通常不需要调用configure_mappers(),因为这个过程是根据需要的,一旦通过属性或查询访问需要映射时就会发生。

#1966

核心监听器/代理被事件监听器取代

PoolListenerConnectionProxyDDLElement.execute_atevent.listen() 取代,分别使用 PoolEventsEngineEventsDDLEvents 分发目标。

ORM 扩展被事件监听器取代

MapperExtensionAttributeExtensionSessionExtensionevent.listen() 取代,分别使用 MapperEvents/InstanceEventsAttributeEventsSessionEvents、分发目标。

为了在 MySQL 中向 select() 中的 ‘distinct’ 发送字符串,应该通过前缀来完成

这个隐晦的特性允许在 MySQL 后端中使用这种模式:

select([mytable], distinct="ALL", prefixes=["HIGH_PRIORITY"])

应该使用 prefixes 关键字或 prefix_with() 方法来处理非标准或不寻常的前缀:

select([mytable]).prefix_with("HIGH_PRIORITY", "ALL")

useexistingextend_existingkeep_existing 取代

Table 上的 useexisting 标志已被一对新标志 keep_existingextend_existing 取代。extend_existing 等同于 useexisting - 返回现有的 Table,并添加额外的构造元素。使用 keep_existing,返回现有的 Table,但不添加额外的构造元素 - 这些元素仅在创建新 Table 时应用。

向后不兼容的 API 更改

传递给 bindparam() 的可调用对象不会被评估 - 影响 Beaker 示例

#1950

请注意,这会影响 Beaker 缓存示例,其中 _params_from_query() 函数的工作方式需要进行轻微调整。如果您正在使用 Beaker 示例中的代码,则应用此更改。

types.type_map 现在是私有的,types._type_map

我们注意到一些用户在 sqlalchemy.types 中利用这个字典作为将 Python 类型与 SQL 类型关联的快捷方式。我们无法保证这个字典的内容或格式,此外,将 Python 类型一对一关联的业务有一些灰色地带,最好由各个应用程序自行决定,因此我们强调了这个属性。

#1870

将独立 alias() 函数的 alias 关键字参数重命名为 name

这样关键字参数 name 就与所有 FromClause 对象上的 alias() 方法以及 Query.subquery() 上的 name 参数匹配了。

只有使用独立的 alias() 函数的代码,而不是绑定方法函数,并且使用显式关键字名称 alias 而不是位置上的别名名称传递的代码需要在这里进行修改。

非公共 Pool 方法已被下划线标记

所有不打算公开使用的 Pool 及其子类方法都已重命名为下划线。之前没有以这种方式命名是一个错误。

现在已经弃用或删除了池化方法:

Pool.create_connection() -> Pool._create_connection()

Pool.do_get() -> Pool._do_get()

Pool.do_return_conn() -> Pool._do_return_conn()

Pool.do_return_invalid() -> 已移除,未被使用

Pool.return_conn() -> Pool._return_conn()

Pool.get() -> Pool._get(), 公共 API 是 Pool.connect()

SingletonThreadPool.cleanup() -> _cleanup()

SingletonThreadPool.dispose_local() -> 已移除,使用 conn.invalidate()

#1982

传递给 bindparam() 的可调用对象不会被评估 - 影响 Beaker 示例

#1950

请注意,这会影响 Beaker 缓存示例,其中 _params_from_query() 函数的工作需要进行轻微调整。如果你正在使用 Beaker 示例中的代码,应用这一变更。

types.type_map 现在是私有的,types._type_map

我们注意到一些用户在 sqlalchemy.types 中利用这个字典作为将 Python 类型与 SQL 类型关联的快捷方式。我们无法保证这个字典的内容或格式,此外,将 Python 类型与 SQL 类型一对一关联的业务有一些灰色地带,最好由各个应用程序自行决定,因此我们已经将这个属性标记为下划线。

#1870

将独立 alias() 函数的 alias 关键字参数重命名为 name

这样做是为了使关键字参数 name 与所有 FromClause 对象上的 alias() 方法以及 Query.subquery() 上的 name 参数匹配。

只有使用独立 alias() 函数的代码,而不是绑定方法函数,并且使用显式关键字名称 alias 而不是位置参数传递别名的代码需要在这里进行修改。

非公共 Pool 方法已被标记下划线

所有 Pool 及其子类的不打算公开使用的方法都已重命名为下划线。它们之前没有以这种方式命名是一个 bug。

池化方法现在已经强调或移除:

Pool.create_connection() -> Pool._create_connection()

Pool.do_get() -> Pool._do_get()

Pool.do_return_conn() -> Pool._do_return_conn()

Pool.do_return_invalid() -> 已移除,未被使用

Pool.return_conn() -> Pool._return_conn()

Pool.get() -> Pool._get(), 公共 API 是 Pool.connect()

SingletonThreadPool.cleanup() -> _cleanup()

SingletonThreadPool.dispose_local() -> 已移除,使用 conn.invalidate()

#1982

之前已弃用,现在已移除

Query.join()、Query.outerjoin()、eagerload()、eagerload_all() 等不再允许作为参数传递属性列表。

自 0.5 版本以来,将属性或属性名称列表传递给 Query.joineagerload() 和类似方法已被弃用:

# old way, deprecated since 0.5
session.query(Houses).join([Houses.rooms, Room.closets])
session.query(Houses).options(eagerload_all([Houses.rooms, Room.closets]))

这些方法自 0.5 系列以来都接受 *args:

# current way, in place since 0.5
session.query(Houses).join(Houses.rooms, Room.closets)
session.query(Houses).options(eagerload_all(Houses.rooms, Room.closets))

ScopedSession.mapper 已被移除

此功能提供了一个映射器扩展,将基于类的功能与特定的ScopedSession关联起来,特别是提供了新对象实例自动与该会话关联的行为。该功能被教程和框架过度使用,由于其隐式行为而导致用户混乱,并在 0.5.5 中被弃用。复制其功能的技术位于[wiki:UsageRecipes/SessionAwareMapper]。

Query.join()Query.outerjoin()eagerload()eagerload_all()等不再接受属性列表作为参数。

自 0.5 版本以来,向Query.joineagerload()等传递属性列表或属性名称已被弃用。

# old way, deprecated since 0.5
session.query(Houses).join([Houses.rooms, Room.closets])
session.query(Houses).options(eagerload_all([Houses.rooms, Room.closets]))

0.5 系列以来,这些方法都接受*args

# current way, in place since 0.5
session.query(Houses).join(Houses.rooms, Room.closets)
session.query(Houses).options(eagerload_all(Houses.rooms, Room.closets))

ScopedSession.mapper已移除。

此功能提供了一个映射器扩展,将基于类的功能与特定的ScopedSession关联起来,特别是提供了新对象实例自动与该会话关联的行为。该功能被教程和框架过度使用,由于其隐式行为而导致用户混乱,并在 0.5.5 中被弃用。复制其功能的技术位于[wiki:UsageRecipes/SessionAwareMapper]。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(六十四)(1)
SqlAlchemy 2.0 中文文档(六十四)
10 0
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(2)
SqlAlchemy 2.0 中文文档(七十六)
17 2
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(1)
SqlAlchemy 2.0 中文文档(七十六)
11 2
|
3天前
|
关系型数据库 测试技术 API
SqlAlchemy 2.0 中文文档(二十八)(3)
SqlAlchemy 2.0 中文文档(二十八)
9 1
|
3天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(二十八)(4)
SqlAlchemy 2.0 中文文档(二十八)
12 1
|
3天前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(八十一)(1)
SqlAlchemy 2.0 中文文档(八十一)
10 2
|
3天前
|
存储 SQL Java
SqlAlchemy 2.0 中文文档(八十一)(4)
SqlAlchemy 2.0 中文文档(八十一)
16 1
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(八十一)(5)
SqlAlchemy 2.0 中文文档(八十一)
15 1
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(八十一)(2)
SqlAlchemy 2.0 中文文档(八十一)
13 1
|
3天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(八十一)(3)
SqlAlchemy 2.0 中文文档(八十一)
13 1