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()
在项目与会话关联之后而不是之前触发 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() )
查询现在像 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)
。
相关性现在始终是上下文特定的
为了允许更广泛的相关性场景,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。
这是一个错误修复,但一些应用程序可能一直依赖于先前的行为。
修复了 InstrumentationEvents
的事件定位。
InstrumentationEvents
系列事件目标已经记录,事件只会根据实际传递的类来触发。在 0.7 版本中,情况并非如此,应用于InstrumentationEvents
的任何事件监听器都会对所有映射的类调用。在 0.8 版本中,添加了额外的逻辑,使事件只会为那些发送的类调用。这里的propagate
标志默认设置为True
,因为类仪器事件通常用于拦截尚未创建的类。
不再将“=”自动转换为 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 通常的范围,因此删除了这种行为。
修复了Session.is_modified()
的行为
Session.is_modified()
方法接受一个参数passive
,基本上不应该是必要的,所有情况下参数应该是值True
- 当保持默认值False
时,会影响数据库,并经常触发自动刷新,这将改变结果。在 0.8 版本中,passive
参数将不起作用,并且未加载的属性永远不会检查历史记录,因为根据定义,未加载的属性上不会有待处理的状态更改。
另请参见
Session.is_modified()
Column.key
在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 之前,如果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 字符时不会出现问题。
single_parent
警告现在是错误
一个relationship()
,它是多对一或多对多关系,并指定了“cascade=’all, delete-orphan’”,这是一个尴尬但仍然支持的用例(带有限制),如果关系没有指定single_parent=True
选项,现在将引发错误。以前只会发出警告,但在任何情况下几乎立即会在属性系统中跟随失败。
将inspector
参数添加到column_reflect
事件
0.7 添加了一个名为column_reflect
的新事件,提供了每个列反射时可以增强的事件。我们在这个事件上稍微出了点错,因为事件没有提供获取当前用于反射的Inspector
和Connection
的方法,以防需要来自数据库的其他信息。由于这是一个尚未广泛使用的新事件,我们将直接在其中添加inspector
参数:
@event.listens_for(Table, "column_reflect") def listen_for_col(inspector, table, column_info): ...
禁用 MySQL 的自动检测排序规则和大小写
MySQL 方言在Engine
连接时第一次进行两次调用,其中一次非常昂贵,加载数据库中的所有可能排序规则以及大小写信息。这两个集合都不会用于任何 SQLAlchemy 函数,因此这些调用将被更改为不再自动发出。可能依赖于这些集合存在于engine.dialect
上的应用程序将需要直接调用_detect_collations()
和_detect_casing()
。
“未使用的列名”警告变为异常
在insert()
或update()
构造中引用不存在的列将引发错误而不是警告:
t1 = table("t1", column("x")) t1.insert().values(x=5, z=5) # raises "Unconsumed column names: z"
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"]}
大多数情况下将禁用不区分大小写的结果行名称
一个非常古老的行为,RowProxy
中的列名始终是不区分大小写比较的:
>>> row = result.fetchone() >>> row["foo"] == row["FOO"] == row["Foo"] True
这是为了一些在早期需要这样做的方言,如 Oracle 和 Firebird,但在现代用法中,我们有更准确的方法来处理这两个平台的不区分大小写行为。
未来,此行为将仅可选地可用,通过将标志case_sensitive=False
传递给create_engine()
,但否则从行中请求的列名必须匹配大小写。
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()
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() )
查询现在会像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)
。
关联现在始终是上下文特定的
为了允许更广泛的关联情景,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 的无效字符串输出的应用程序,最有可能是在测试场景中,才会看到任何变化。
create_all() 和 drop_all() 现在将尊重一个空列表
方法MetaData.create_all()
和MetaData.drop_all()
现在将接受一个空的Table
对象列表,并且不会发出任何 CREATE 或 DROP 语句。以前,空列表被解释为传递None
给一个集合,对于所有项目都会无条件发出 CREATE/DROP。
这是一个 bug 修复,但某些应用可能一直依赖于以前的行为。
修复了 InstrumentationEvents
的事件定位
InstrumentationEvents
系列事件目标已经记录,事件将根据实际传递的类来触发。直到 0.7 版本,这并不是这样,任何应用于 InstrumentationEvents
的事件监听器都会对所有映射的类调用。在 0.8 中,添加了额外的逻辑,使事件只会为那些传递的类调用。这里的 propagate
标志默认设置为 True
,因为类仪器事件通常用于拦截尚未创建的类。
不再将“=”在 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 通常的范围,因此已移除该行为。
修复了 Session.is_modified()
的行为
Session.is_modified()
方法接受一个参数 passive
,基本上不应该是必要的,所有情况下参数应该是值 True
- 当保持默认值 False
时,它会影响到数据库,并经常触发自动刷新,这将改变结果。在 0.8 中,passive
参数将不会产生任何影响,并且未加载的属性永远不会被检查历史,因为根据定义,未加载的属性不会有待处理的状态更改。
另请参阅
Session.is_modified()
Column.key
在 select()
的 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 版本之前,如果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 字符时出现问题。
single_parent
警告现在变成了错误
一个relationship()
,它是多对一或多对多关系,并指定“cascade=‘all, delete-orphan’”,这是一个尴尬但仍然支持的用例(受限制),如果关系没有指定single_parent=True
选项,现在将引发错误。以前它只会发出警告,但在任何情况下几乎立即会在属性系统中跟随失败。
将inspector
参数添加到column_reflect
事件中
0.7 版本添加了一个名为column_reflect
的新事件,提供了对每个反射的列进行增强的方式。我们在这个事件中稍微出了点错,因为事件没有提供访问当前用于反射的Inspector
和Connection
的方法,以防需要来自数据库的附加信息。由于这是一个尚未广泛使用的新事件,我们将直接在其中添加inspector
参数:
@event.listens_for(Table, "column_reflect") def listen_for_col(inspector, table, column_info): ...
禁用 MySQL 的自动检测排序规则和大小写敏感性
MySQL 方言进行两次调用,其中一次非常昂贵,从数据库加载所有可能的排序规则以及大小写敏感性的信息,第一次引擎连接时。这两个集合都不会被任何 SQLAlchemy 函数使用,因此这些调用将被更改为不再自动发出。可能依赖于这些集合存在于engine.dialect
上的应用程序将需要直接调用_detect_collations()
和_detect_casing()
。
“未消耗的列名” 警告变为异常
在 insert()
或 update()
构造中引用不存在的列将引发错误而不是警告:
t1 = table("t1", column("x")) t1.insert().values(x=5, z=5) # raises "Unconsumed column names: z"
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"]}
大多数情况下将禁用不区分大小写的结果行名称
一个非常古老的行为,RowProxy
中的列名总是不区分大小写地进行比较:
>>> row = result.fetchone() >>> row["foo"] == row["FOO"] == row["Foo"] True
这是为了一些在早期需要这样做的方言的利益,比如 Oracle 和 Firebird,但在现代用法中,我们有更准确的方法来处理这两个平台的不区分大小写行为。
未来,此行为将仅可选地通过向 create_engine()
传递标志 case_sensitive=False
来使用,但否则从行中请求的列名必须匹配大小写。
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 是一个非常简单的工具,也可以受益于对其使用方式感兴趣的贡献者。
可变类型
SQLAlchemy ORM 中的旧的“可变”系统已经移除。这指的是应用于诸如PickleType
的类型和有条件地应用于TypeDecorator
的MutableType
接口,自很早的 SQLAlchemy 版本以来一直提供了一种让 ORM 检测所谓的“可变”数据结构(如 JSON 结构和 pickled 对象)变化的方式。然而,该实现从未合理,强制单元操作工作在非常低效的模式下运行,导致在 flush 期间对所有对象进行昂贵的扫描。在 0.7 版本中,引入了sqlalchemy.ext.mutable扩展,以便用户定义的数据类型可以在发生更改时适当地向单元操作发送事件。
今天,MutableType
的使用预计会很少,因为多年来已经发出了有关其低效性的警告。
sqlalchemy.exceptions(多年来一直是 sqlalchemy.exc)
我们保留了别名sqlalchemy.exceptions
,以尝试使一些非常旧的库稍微容易些,这些库尚未升级以使用sqlalchemy.exc
。然而,一些用户仍然被困惑,因此在 0.8 版本中,我们将其完全删除,以消除任何困惑。
SQLSoup
SQLSoup 是一个方便的包,它在 SQLAlchemy ORM 之上提供了一个替代接口。SQLSoup 现在已移动到自己的项目中,并且进行了单独的文档编写/发布;请参阅bitbucket.org/zzzeek/sqlsoup
。
SQLSoup 是一个非常简单的工具,也可以从对其使用风格感兴趣的贡献者中受益。
MutableType
SQLAlchemy ORM 中的旧的“可变”系统已经移除。这指的是应用于诸如PickleType
的类型和有条件地应用于TypeDecorator
的MutableType
接口,自很早的 SQLAlchemy 版本以来一直提供了一种让 ORM 检测所谓的“可变”数据结构(如 JSON 结构和 pickled 对象)变化的方式。然而,该实现从未合理,强制单元操作工作在非常低效的模式下运行,导致在 flush 期间对所有对象进行昂贵的扫描。在 0.7 版本中,引入了sqlalchemy.ext.mutable扩展,以便用户定义的数据类型可以在发生更改时适当地向单元操作发送事件。
今天,MutableType
的使用预计会很少,因为多年来已经发出了有关其低效性的警告。
sqlalchemy.exceptions(多年来一直是 sqlalchemy.exc)
我们曾经在别名sqlalchemy.exceptions
中尝试让一些非常老旧的库更容易使用sqlalchemy.exc
。然而,一些用户仍然感到困惑,因此在 0.8 版本中,我们将完全删除它,以消除任何困惑。