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

本文涉及的产品
云原生数据库 PolarDB PostgreSQL 版,企业版 4核16GB
推荐场景:
HTAP混合负载
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
简介: SqlAlchemy 2.0 中文文档(七十八)

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


行为变化

将“待定”对象视为“孤儿”的考虑变得更加积极

这是 0.8 系列的一个后期添加,但希望新行为在更广泛的情况下更一致和直观。ORM 自至少 0.4 版本以来一直包括这样的行为,即一个“待定”对象,意味着它与Session关联但尚未插入数据库,当它成为“孤儿”时,即已与使用delete-orphan级联的父对象解除关联时,将自动从Session中清除。此行为旨在大致模拟持久对象的行为,其中 ORM 将根据分离事件的拦截发出 DELETE 以删除这些成为孤儿的对象。

行为变化适用于被多种父对象引用并且每个父对象都指定delete-orphan的对象;典型示例是在多对多模式中连接两种其他对象的关联对象。以前,行为是这样的,即当待定对象与所有父对象解除关联时才会被清除。随着行为变化,一旦待定对象与任何先前关联的父对象解除关联,该待定对象就会被清除。此行为旨在更接近持久对象的行为,即一旦与任何父对象解除关联,它们就会被删除。

较旧行为的基本原因可以追溯到至少版本 0.4,基本上是一种防御性决定,试图在对象仍在为 INSERT 构造时减轻混淆。但现实情况是,无论如何,只要对象附加到任何新父级,它就会立即重新与Session关联。

仍然可以刷新一个对象,该对象尚未与其所有必需的父级关联,如果该对象一开始就未与这些父级关联,或者如果它被清除,但随后通过后续附加事件重新与Session关联,但仍未完全关联。在这种情况下,预计数据库会发出完整性错误,因为很可能存在未填充的  NOT NULL 外键列。ORM 做出决定让这些 INSERT  尝试发生,基于这样的判断:一个只与其必需的父级部分关联但已经与其中一些父级积极关联的对象,更多的情况下是用户错误,而不是应该被悄悄跳过的有意遗漏  - 在这里悄悄跳过 INSERT 会使这种性质的用户错误非常难以调试。

对于可能依赖于旧行为的应用程序,可以通过将标志legacy_is_orphan指定为映射器选项来重新启用旧行为。

新行为允许以下测试用例正常工作:

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True)
    name = Column(String(64))
class UserKeyword(Base):
    __tablename__ = "user_keyword"
    user_id = Column(Integer, ForeignKey("user.id"), primary_key=True)
    keyword_id = Column(Integer, ForeignKey("keyword.id"), primary_key=True)
    user = relationship(
        User, backref=backref("user_keywords", cascade="all, delete-orphan")
    )
    keyword = relationship(
        "Keyword", backref=backref("user_keywords", cascade="all, delete-orphan")
    )
    # uncomment this to enable the old behavior
    # __mapper_args__ = {"legacy_is_orphan": True}
class Keyword(Base):
    __tablename__ = "keyword"
    id = Column(Integer, primary_key=True)
    keyword = Column("keyword", String(64))
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
# note we're using PostgreSQL to ensure that referential integrity
# is enforced, for demonstration purposes.
e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)
session = Session(e)
u1 = User(name="u1")
k1 = Keyword(keyword="k1")
session.add_all([u1, k1])
uk1 = UserKeyword(keyword=k1, user=u1)
# previously, if session.flush() were called here,
# this operation would succeed, but if session.flush()
# were not called here, the operation fails with an
# integrity error.
# session.flush()
del u1.user_keywords[0]
session.commit()

#2655

在项目与会话关联之后而不是之前触发 after_attach 事件;before_attach 添加

使用 after_attach 的事件处理程序现在可以假定给定实例与给定会话关联:

@event.listens_for(Session, "after_attach")
def after_attach(session, instance):
    assert instance in session

有些用例要求它按照这种方式工作。然而,其他用例要求该项尚未成为会话的一部分,例如,当一个查询旨在加载实例所需的某些状态时,首先会触发自动刷新并且否则会过早刷新目标对象。这些用例应该使用新的“before_attach”事件:

@event.listens_for(Session, "before_attach")
def before_attach(session, instance):
    instance.some_necessary_attribute = (
        session.query(Widget).filter_by(instance.widget_name).first()
    )

#2464

查询现在像 select()一样自动相关

以前需要调用Query.correlate()才能使列或 WHERE 子查询与父级相关联:

subq = (
    session.query(Entity.value)
    .filter(Entity.id == Parent.entity_id)
    .correlate(Parent)
    .as_scalar()
)
session.query(Parent).filter(subq == "some value")

这与普通的select()构造相反,后者默认情况下会自动相关。在 0.8 中,上述语句将自动相关:

subq = session.query(Entity.value).filter(Entity.id == Parent.entity_id).as_scalar()
session.query(Parent).filter(subq == "some value")

就像在select()中一样,通过调用query.correlate(None)来禁用相关性,或者通过传递一个实体来手动设置相关性,query.correlate(someentity)

#2179

相关性现在始终是上下文特定的

为了允许更广泛的相关性场景,Select.correlate()Query.correlate() 的行为略有改变,即如果 SELECT 语句实际上在该上下文中使用,那么该语句将仅在 FROM 子句中省略“相关”的目标。此外,不再可能将作为外部 SELECT 语句中的 FROM 的 SELECT 语句“相关”(即省略)FROM 子句。

这个改变只会让 SQL 渲染变得更好,因为不再可能渲染出不合法的 SQL,即在选择的内容相对于 FROM 对象不足的情况下:

from sqlalchemy.sql import table, column, select
t1 = table("t1", column("x"))
t2 = table("t2", column("y"))
s = select([t1, t2]).correlate(t1)
print(s)

在此更改之前,上述将返回:

SELECT  t1.x,  t2.y  FROM  t2

当“t1”在任何 FROM 子句中都没有被引用时,这是无效的 SQL。

现在,在没有外部 SELECT 的情况下,它返回:

SELECT  t1.x,  t2.y  FROM  t1,  t2

在 SELECT 中,相关性会如预期地生效:

s2 = select([t1, t2]).where(t1.c.x == t2.c.y).where(t1.c.x == s)
print(s2)
SELECT  t1.x,  t2.y  FROM  t1,  t2
WHERE  t1.x  =  t2.y  AND  t1.x  =
  (SELECT  t1.x,  t2.y  FROM  t2)

这个改变不会影响任何现有应用程序,因为对于正确构建的表达式,相关性行为保持不变。只有依赖于在非相关上下文中使用相关的 SELECT 的无效字符串输出的应用程序(很可能是在测试场景中),才会看到任何变化。

#2668 ### create_all() 和 drop_all() 现在将空列表视为如此

方法 MetaData.create_all()MetaData.drop_all() 现在将接受一个空列表的 Table 对象,并且不会发出任何 CREATE 或 DROP 语句。以前,将空列表解释为对集合传递 None,并且将无条件地为所有项目发出 CREATE/DROP。

这是一个错误修复,但一些应用程序可能一直依赖于先前的行为。

#2664

修复了 InstrumentationEvents 的事件定位。

InstrumentationEvents系列事件目标已经记录,事件只会根据实际传递的类来触发。在 0.7 版本中,情况并非如此,应用于InstrumentationEvents的任何事件监听器都会对所有映射的类调用。在 0.8 版本中,添加了额外的逻辑,使事件只会为那些发送的类调用。这里的propagate标志默认设置为True,因为类仪器事件通常用于拦截尚未创建的类。

#2590

不再将“=”自动转换为 IN,用于与 MS-SQL 中的子查询进行比较

我们在 MSSQL 方言中发现了一个非常古老的行为,当用户执行类似以下操作时,它会试图拯救用户:

scalar_subq = select([someothertable.c.id]).where(someothertable.c.data == "foo")
select([sometable]).where(sometable.c.id == scalar_subq)

SQL Server 不允许将相等比较与标量 SELECT 进行比较,即“x = (SELECT something)”。MSSQL  方言会将其转换为 IN。然而,当进行类似“(SELECT something) =  x”的比较时,也会发生相同的情况,总体而言,这种猜测的水平超出了 SQLAlchemy 通常的范围,因此删除了这种行为。

#2277

修复了Session.is_modified()的行为

Session.is_modified()方法接受一个参数passive,基本上不应该是必要的,所有情况下参数应该是值True - 当保持默认值False时,会影响数据库,并经常触发自动刷新,这将改变结果。在 0.8 版本中,passive参数将不起作用,并且未加载的属性永远不会检查历史记录,因为根据定义,未加载的属性上不会有待处理的状态更改。

另请参见

Session.is_modified()

#2320

Column.keySelect.c属性中受到Select.apply_labels()的尊重

表达式系统的用户知道Select.apply_labels()会在每个列名前添加表名,影响从Select.c中可用的名称:

s = select([table1]).apply_labels()
s.c.table1_col1
s.c.table1_col2

在 0.8 之前,如果Column具有不同的Column.key,则此键将被忽略,与未使用Select.apply_labels()时的不一致性:

# before 0.8
table1 = Table("t1", metadata, Column("col1", Integer, key="column_one"))
s = select([table1])
s.c.column_one  # would be accessible like this
s.c.col1  # would raise AttributeError
s = select([table1]).apply_labels()
s.c.table1_column_one  # would raise AttributeError
s.c.table1_col1  # would be accessible like this

在 0.8 中,Column.key在两种情况下都受到尊重:

# with 0.8
table1 = Table("t1", metadata, Column("col1", Integer, key="column_one"))
s = select([table1])
s.c.column_one  # works
s.c.col1  # AttributeError
s = select([table1]).apply_labels()
s.c.table1_column_one  # works
s.c.table1_col1  # AttributeError

关于“name”和“key”的所有其他行为都是相同的,包括渲染的 SQL 仍然使用形式<tablename>_<colname> - 这里的重点是防止Column.key内容被渲染到SELECT语句中,以便在Column.key中使用特殊/非 ASCII 字符时不会出现问题。

#2397

single_parent警告现在是错误

一个relationship(),它是多对一或多对多关系,并指定了“cascade=’all, delete-orphan’”,这是一个尴尬但仍然支持的用例(带有限制),如果关系没有指定single_parent=True选项,现在将引发错误。以前只会发出警告,但在任何情况下几乎立即会在属性系统中跟随失败。

#2405

inspector参数添加到column_reflect事件

0.7 添加了一个名为column_reflect的新事件,提供了每个列反射时可以增强的事件。我们在这个事件上稍微出了点错,因为事件没有提供获取当前用于反射的InspectorConnection的方法,以防需要来自数据库的其他信息。由于这是一个尚未广泛使用的新事件,我们将直接在其中添加inspector参数:

@event.listens_for(Table, "column_reflect")
def listen_for_col(inspector, table, column_info): ...

#2418

禁用 MySQL 的自动检测排序规则和大小写

MySQL 方言在Engine连接时第一次进行两次调用,其中一次非常昂贵,加载数据库中的所有可能排序规则以及大小写信息。这两个集合都不会用于任何 SQLAlchemy 函数,因此这些调用将被更改为不再自动发出。可能依赖于这些集合存在于engine.dialect上的应用程序将需要直接调用_detect_collations()_detect_casing()

#2404

“未使用的列名”警告变为异常

insert()update()构造中引用不存在的列将引发错误而不是警告:

t1 = table("t1", column("x"))
t1.insert().values(x=5, z=5)  # raises "Unconsumed column names: z"

#2415

Inspector.get_primary_keys()已弃用,请使用 Inspector.get_pk_constraint

这两种Inspector上的方法是多余的,其中get_primary_keys()将返回与get_pk_constraint()相同的信息,只是不包括约束的名称:

>>> insp.get_primary_keys()
["a", "b"]
>>> insp.get_pk_constraint()
{"name":"pk_constraint", "constrained_columns":["a", "b"]}

#2422

大多数情况下将禁用不区分大小写的结果行名称

一个非常古老的行为,RowProxy中的列名始终是不区分大小写比较的:

>>> row = result.fetchone()
>>> row["foo"] == row["FOO"] == row["Foo"]
True

这是为了一些在早期需要这样做的方言,如 Oracle 和 Firebird,但在现代用法中,我们有更准确的方法来处理这两个平台的不区分大小写行为。

未来,此行为将仅可选地可用,通过将标志case_sensitive=False传递给create_engine(),但否则从行中请求的列名必须匹配大小写。

#2423

InstrumentationManager和替代类仪器现在是一个扩展

sqlalchemy.orm.interfaces.InstrumentationManager类已移至sqlalchemy.ext.instrumentation.InstrumentationManager。 “替代仪器”系统是为了极少数需要使用现有或不寻常的类仪器系统的安装而构建的,并且通常很少使用。该系统的复杂性已导出到一个ext.模块中。直到被导入后才会使用,通常是当第三方库导入InstrumentationManager时,此时它将通过用ExtendedInstrumentationRegistry替换默认的InstrumentationFactory将其注入回sqlalchemy.orm中。

将“待定”对象视为“孤儿”的考虑变得更加激进

这是 0.8 系列的一个较晚添加,但希望新行为在更广泛的情况下通常更一致和直观。ORM 自至少 0.4 版本以来已经包含了这样的行为,即一个“待定”对象,意味着它与Session相关联,但尚未插入到数据库中,当它成为“孤儿”时,即已经与引用它的父对象解除关联,并且在配置的relationship()上使用delete-orphan级联时,将自动从Session中删除。此行为旨在大致反映持久对象(即已插入)的行为,ORM 将根据分离事件的拦截为这些成为孤儿的对象发出 DELETE。

行为变更适用于被多种类型父对象引用的对象,每种类型父对象都指定delete-orphan;典型示例是在多对多模式中桥接两种其他对象的关联对象。以前的行为是,挂起的对象仅在与所有父对象解除关联时才会被清除。通过行为变更,只要挂起的对象与先前相关联的任何父对象解除关联,它就会被清除。这种行为旨在更接近持久对象的行为,即只要它们与任何父对象解除关联,它们就会被删除。

较旧行为的基本理由可以追溯到至少版本 0.4,基本上是一种防御性决定,试图在对象仍在为 INSERT 构造时减轻混淆。但事实是,无论如何,只要对象附加到任何新父对象,它就会立即重新与Session相关联。

仍然可以刷新一个与所有必需父对象都不相关联的对象,如果该对象一开始就没有与这些父对象相关联,或者如果它被清除,但后来通过后续的附加事件重新与Session相关联但仍未完全相关联。在这种情况下,预计数据库会发出完整性错误,因为可能存在未填充的  NOT NULL 外键列。ORM 决定让这些 INSERT  尝试发生,基于这样的判断:一个只与其必需父对象部分相关联但已经积极与其中一些相关联的对象,往往更多是用户错误,而不是应该被悄悄跳过的有意遗漏 -  在这里悄悄跳过 INSERT 会使这种用户错误非常难以调试。

对于可能依赖于旧行为的应用程序,可以通过将标志legacy_is_orphan作为映射器选项指定,重新启用任何Mapper的旧行为。

新行为允许以下测试用例工作:

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True)
    name = Column(String(64))
class UserKeyword(Base):
    __tablename__ = "user_keyword"
    user_id = Column(Integer, ForeignKey("user.id"), primary_key=True)
    keyword_id = Column(Integer, ForeignKey("keyword.id"), primary_key=True)
    user = relationship(
        User, backref=backref("user_keywords", cascade="all, delete-orphan")
    )
    keyword = relationship(
        "Keyword", backref=backref("user_keywords", cascade="all, delete-orphan")
    )
    # uncomment this to enable the old behavior
    # __mapper_args__ = {"legacy_is_orphan": True}
class Keyword(Base):
    __tablename__ = "keyword"
    id = Column(Integer, primary_key=True)
    keyword = Column("keyword", String(64))
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
# note we're using PostgreSQL to ensure that referential integrity
# is enforced, for demonstration purposes.
e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)
session = Session(e)
u1 = User(name="u1")
k1 = Keyword(keyword="k1")
session.add_all([u1, k1])
uk1 = UserKeyword(keyword=k1, user=u1)
# previously, if session.flush() were called here,
# this operation would succeed, but if session.flush()
# were not called here, the operation fails with an
# integrity error.
# session.flush()
del u1.user_keywords[0]
session.commit()

#2655

after_attach 事件在项目与会话关联后而不是之前触发;添加了 before_attach

使用 after_attach 的事件处理程序现在可以假定给定的实例与给定的会话相关联:

@event.listens_for(Session, "after_attach")
def after_attach(session, instance):
    assert instance in session

有些用例要求它按照这种方式工作。然而,其他用例要求该项尚未成为会话的一部分,比如当一个查询,旨在加载实例所需的某些状态时,首先发出自动刷新,否则会过早刷新目标对象。这些用例应该使用新的“before_attach”事件:

@event.listens_for(Session, "before_attach")
def before_attach(session, instance):
    instance.some_necessary_attribute = (
        session.query(Widget).filter_by(instance.widget_name).first()
    )

#2464

查询现在会像select()一样自动关联

以前需要调用Query.correlate()才能使列或 WHERE 子查询与父级关联:

subq = (
    session.query(Entity.value)
    .filter(Entity.id == Parent.entity_id)
    .correlate(Parent)
    .as_scalar()
)
session.query(Parent).filter(subq == "some value")

这是一个普通 select() 构造的相反行为,默认情况下会假定自动关联。在 0.8 中,上述语句将自动关联:

subq = session.query(Entity.value).filter(Entity.id == Parent.entity_id).as_scalar()
session.query(Parent).filter(subq == "some value")

就像在 select() 中一样,可以通过调用 query.correlate(None) 来禁用关联,或者通过传递一个实体来手动设置,query.correlate(someentity)

#2179

关联现在始终是上下文特定的

为了允许更广泛的关联情景,Select.correlate()Query.correlate() 的行为略有变化,这样 SELECT 语句将仅在实际使用它在该上下文中时,从 FROM 子句中省略“相关”的目标。此外,不再可能在一个封闭的 SELECT 语句中作为 FROM 放置一个 SELECT 语句来“关联”(即省略)一个 FROM 子句。

这个变化只会使 SQL 渲染变得更好,因为不再可能渲染出非法的 SQL,其中选择的 FROM 对象不足:

from sqlalchemy.sql import table, column, select
t1 = table("t1", column("x"))
t2 = table("t2", column("y"))
s = select([t1, t2]).correlate(t1)
print(s)

在这个变化之前,上述内容会返回:

SELECT  t1.x,  t2.y  FROM  t2

这是无效的 SQL,因为“t1”在任何 FROM 子句中都没有被引用。

现在,在没有封闭 SELECT 的情况下,它返回:

SELECT  t1.x,  t2.y  FROM  t1,  t2

在 SELECT 中,关联会按预期生效:

s2 = select([t1, t2]).where(t1.c.x == t2.c.y).where(t1.c.x == s)
print(s2)
SELECT  t1.x,  t2.y  FROM  t1,  t2
WHERE  t1.x  =  t2.y  AND  t1.x  =
  (SELECT  t1.x,  t2.y  FROM  t2)

这个变化预计不会影响任何现有的应用程序,因为相关性行为对于正确构建的表达式保持不变。只有一个依赖于在非相关上下文中使用相关 SELECT 的无效字符串输出的应用程序,最有可能是在测试场景中,才会看到任何变化。

#2668

create_all() 和 drop_all() 现在将尊重一个空列表

方法MetaData.create_all()MetaData.drop_all()现在将接受一个空的Table对象列表,并且不会发出任何 CREATE 或 DROP 语句。以前,空列表被解释为传递None给一个集合,对于所有项目都会无条件发出 CREATE/DROP。

这是一个 bug 修复,但某些应用可能一直依赖于以前的行为。

#2664

修复了 InstrumentationEvents 的事件定位

InstrumentationEvents 系列事件目标已经记录,事件将根据实际传递的类来触发。直到 0.7 版本,这并不是这样,任何应用于 InstrumentationEvents 的事件监听器都会对所有映射的类调用。在 0.8 中,添加了额外的逻辑,使事件只会为那些传递的类调用。这里的 propagate 标志默认设置为 True,因为类仪器事件通常用于拦截尚未创建的类。

#2590

不再将“=”在 MS-SQL 中与子查询比较时自动转换为 IN

我们在 MSSQL 方言中发现了一个非常古老的行为,当用户尝试做类似这样的事情时,它会试图拯救用户:

scalar_subq = select([someothertable.c.id]).where(someothertable.c.data == "foo")
select([sometable]).where(sometable.c.id == scalar_subq)

SQL Server 不允许将等号与标量 SELECT 进行比较,即,“x = (SELECT something)”。MSSQL  方言会将其转换为 IN。然而,当进行类似“(SELECT something) =  x”的比较时,也会发生同样的情况,总体而言,这种猜测的水平超出了 SQLAlchemy 通常的范围,因此已移除该行为。

#2277

修复了 Session.is_modified() 的行为

Session.is_modified() 方法接受一个参数 passive,基本上不应该是必要的,所有情况下参数应该是值 True - 当保持默认值 False 时,它会影响到数据库,并经常触发自动刷新,这将改变结果。在 0.8 中,passive 参数将不会产生任何影响,并且未加载的属性永远不会被检查历史,因为根据定义,未加载的属性不会有待处理的状态更改。

另请参阅

Session.is_modified()

#2320

Column.keyselect()Select.c 属性中通过 Select.apply_labels() 得到尊重

表达式系统的用户知道Select.apply_labels()会在每个列名前面添加表名,影响从Select.c中可用的名称:

s = select([table1]).apply_labels()
s.c.table1_col1
s.c.table1_col2

在 0.8 版本之前,如果ColumnColumn.key不同,这个键会被忽略,与未使用Select.apply_labels()时不一致:

# before 0.8
table1 = Table("t1", metadata, Column("col1", Integer, key="column_one"))
s = select([table1])
s.c.column_one  # would be accessible like this
s.c.col1  # would raise AttributeError
s = select([table1]).apply_labels()
s.c.table1_column_one  # would raise AttributeError
s.c.table1_col1  # would be accessible like this

在 0.8 版本中,Column.key在两种情况下都受到尊重:

# with 0.8
table1 = Table("t1", metadata, Column("col1", Integer, key="column_one"))
s = select([table1])
s.c.column_one  # works
s.c.col1  # AttributeError
s = select([table1]).apply_labels()
s.c.table1_column_one  # works
s.c.table1_col1  # AttributeError

关于“name”和“key”的所有其他行为都是相同的,包括渲染的 SQL 仍然会使用<tablename>_<colname>的形式 - 这里的重点是防止Column.key的内容被渲染到SELECT语句中,以避免在Column.key中使用特殊/非 ASCII 字符时出现问题。

#2397

single_parent警告现在变成了错误

一个relationship(),它是多对一或多对多关系,并指定“cascade=‘all, delete-orphan’”,这是一个尴尬但仍然支持的用例(受限制),如果关系没有指定single_parent=True选项,现在将引发错误。以前它只会发出警告,但在任何情况下几乎立即会在属性系统中跟随失败。

#2405

inspector参数添加到column_reflect事件中

0.7 版本添加了一个名为column_reflect的新事件,提供了对每个反射的列进行增强的方式。我们在这个事件中稍微出了点错,因为事件没有提供访问当前用于反射的InspectorConnection的方法,以防需要来自数据库的附加信息。由于这是一个尚未广泛使用的新事件,我们将直接在其中添加inspector参数:

@event.listens_for(Table, "column_reflect")
def listen_for_col(inspector, table, column_info): ...

#2418

禁用 MySQL 的自动检测排序规则和大小写敏感性

MySQL 方言进行两次调用,其中一次非常昂贵,从数据库加载所有可能的排序规则以及大小写敏感性的信息,第一次引擎连接时。这两个集合都不会被任何 SQLAlchemy 函数使用,因此这些调用将被更改为不再自动发出。可能依赖于这些集合存在于engine.dialect上的应用程序将需要直接调用_detect_collations()_detect_casing()

#2404

“未消耗的列名” 警告变为异常

insert()update() 构造中引用不存在的列将引发错误而不是警告:

t1 = table("t1", column("x"))
t1.insert().values(x=5, z=5)  # raises "Unconsumed column names: z"

#2415

Inspector.get_primary_keys() 已弃用,请使用 Inspector.get_pk_constraint

Inspector 上的这两种方法是多余的,get_primary_keys() 将返回与 get_pk_constraint() 相同的信息,但不包括约束的名称:

>>> insp.get_primary_keys()
["a", "b"]
>>> insp.get_pk_constraint()
{"name":"pk_constraint", "constrained_columns":["a", "b"]}

#2422

大多数情况下将禁用不区分大小写的结果行名称

一个非常古老的行为,RowProxy 中的列名总是不区分大小写地进行比较:

>>> row = result.fetchone()
>>> row["foo"] == row["FOO"] == row["Foo"]
True

这是为了一些在早期需要这样做的方言的利益,比如 Oracle 和 Firebird,但在现代用法中,我们有更准确的方法来处理这两个平台的不区分大小写行为。

未来,此行为将仅可选地通过向 create_engine() 传递标志 case_sensitive=False 来使用,但否则从行中请求的列名必须匹配大小写。

#2423

InstrumentationManager 和替代类仪器现在是一个扩展

sqlalchemy.orm.interfaces.InstrumentationManager 类已移动到 sqlalchemy.ext.instrumentation.InstrumentationManager。 “替代仪器”系统是为了极少数需要使用现有或不寻常的类仪器系统的安装而构建的,并且通常很少使用。 这个系统的复杂性已导出到一个 ext. 模块中。 它在导入一次后保持未使用,通常是在第三方库导入 InstrumentationManager 时,此时通过用 ExtendedInstrumentationRegistry 替换默认的 InstrumentationFactory 将其注入回 sqlalchemy.orm

已移除

SQLSoup

SQLSoup 是一个方便的包,它在 SQLAlchemy ORM 之上提供了一个替代接口。 SQLSoup 现在已移至其自己的项目,并单独进行了文档化/发布;请参见 bitbucket.org/zzzeek/sqlsoup

SQLSoup 是一个非常简单的工具,也可以受益于对其使用方式感兴趣的贡献者。

#2262

可变类型

SQLAlchemy ORM 中的旧的“可变”系统已经移除。这指的是应用于诸如PickleType的类型和有条件地应用于TypeDecoratorMutableType接口,自很早的  SQLAlchemy 版本以来一直提供了一种让 ORM 检测所谓的“可变”数据结构(如 JSON 结构和 pickled  对象)变化的方式。然而,该实现从未合理,强制单元操作工作在非常低效的模式下运行,导致在 flush 期间对所有对象进行昂贵的扫描。在 0.7  版本中,引入了sqlalchemy.ext.mutable扩展,以便用户定义的数据类型可以在发生更改时适当地向单元操作发送事件。

今天,MutableType的使用预计会很少,因为多年来已经发出了有关其低效性的警告。

#2442

sqlalchemy.exceptions(多年来一直是 sqlalchemy.exc)

我们保留了别名sqlalchemy.exceptions,以尝试使一些非常旧的库稍微容易些,这些库尚未升级以使用sqlalchemy.exc。然而,一些用户仍然被困惑,因此在 0.8 版本中,我们将其完全删除,以消除任何困惑。

#2433

SQLSoup

SQLSoup 是一个方便的包,它在 SQLAlchemy ORM 之上提供了一个替代接口。SQLSoup 现在已移动到自己的项目中,并且进行了单独的文档编写/发布;请参阅bitbucket.org/zzzeek/sqlsoup

SQLSoup 是一个非常简单的工具,也可以从对其使用风格感兴趣的贡献者中受益。

#2262

MutableType

SQLAlchemy ORM 中的旧的“可变”系统已经移除。这指的是应用于诸如PickleType的类型和有条件地应用于TypeDecoratorMutableType接口,自很早的  SQLAlchemy 版本以来一直提供了一种让 ORM 检测所谓的“可变”数据结构(如 JSON 结构和 pickled  对象)变化的方式。然而,该实现从未合理,强制单元操作工作在非常低效的模式下运行,导致在 flush 期间对所有对象进行昂贵的扫描。在 0.7  版本中,引入了sqlalchemy.ext.mutable扩展,以便用户定义的数据类型可以在发生更改时适当地向单元操作发送事件。

今天,MutableType的使用预计会很少,因为多年来已经发出了有关其低效性的警告。

#2442

sqlalchemy.exceptions(多年来一直是 sqlalchemy.exc)

我们曾经在别名sqlalchemy.exceptions中尝试让一些非常老旧的库更容易使用sqlalchemy.exc。然而,一些用户仍然感到困惑,因此在 0.8 版本中,我们将完全删除它,以消除任何困惑。

#2433

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