SQLAlchemy 0.6有什么新功能?

优质
小牛编辑
136浏览
2023-12-01

关于此文档

本文档描述了2010年1月16日最后发布的SQLAlchemy版本0.5和2012年5月5日最后发布的SQLAlchemy版本0.6之间的更改。

文件日期:2010年6月6日

本指南记录了影响用户将其应用程序从0.5系列SQLAlchemy迁移到0.6的API更改。请注意,sqlAlchemy 0.6删除了在整个0.5系列中被否决的一些行为,并且还取消了针对0.5的更多行为。

平台支撑

  • 贯穿2.xx系列的CPython 2.4及以上版本

  • Jython2.5.1-使用Jython附带的zxjdbc dbapi。

  • CPython 3.x-见 [source:sqlalchemy/trunk/README.py3k] 有关如何为python3构建的信息。

新方言系统

方言模块现在被分解成不同的子组件,在单个数据库后端的范围内。方言实现现在位于 sqlalchemy.dialects 包裹。这个 sqlalchemy.databases 包仍然作为占位符存在,为简单的导入提供某种程度的向后兼容性。

对于每个支持的数据库,都有一个子包存在于 sqlalchemy.dialects 其中包含多个文件。每个包都包含一个名为 base.py 它定义该数据库使用的特定SQL方言。它还包含一个或多个“驱动程序”模块,每个模块对应一个特定的DBAPI—这些文件的命名与DBAPI本身相对应,例如 pysqlitecx_oraclepyodbc . sqlAlchemy方言使用的类首先在 base.py 模块,定义数据库定义的所有行为特征。其中包括功能映射,如“支持序列”、“支持返回”等、类型定义和SQL编译规则。每个“驱动程序”模块依次根据需要提供这些类的子类,这些子类重写默认行为以适应该DBAPI的其他特性、行为和特性。对于支持多个后端(pyodbc、zxjdbc、mxodbc)的dbapis,方言模块将使用来自 sqlalchemy.connectors 包,它在所有后端为DBAPI提供通用的功能,通常处理连接参数。这意味着使用pyodbc、zxjdbc或mxodbc(在实现时)进行连接在受支持的后端非常一致。

使用的URL格式 create_engine() 已经被增强以处理特定后端的任意数量的DBAPI,使用的方案是受JDBC启发的。以前的格式仍然有效,并将选择“默认”DBAPI实现,例如下面将使用psycopg2的PostgreSQL URL:

create_engine('postgresql://scott:tiger@localhost/test')

但是,要指定特定的DBAPI后端(如PG8000),请使用加号“+”将其添加到URL的“协议”部分:

create_engine('postgresql+pg8000://scott:tiger@localhost/test')

重要方言链接:

关于方言的其他注释:

  • 类型系统在sqlacalchemy 0.6中发生了巨大的变化。这对所有有关命名约定、行为和实现的方言都有影响。请参见下面关于“类型”的部分。

  • 这个 ResultProxy 在某些情况下,由于一些重构,对象现在提供了2倍的速度改进。

  • 这个 RowProxy 即单个结果行对象,现在可以直接进行pickle。

  • 用于定位外部方言的设置工具入口点现在称为 sqlalchemy.dialects . 在任何情况下,都需要修改针对0.4或0.5编写的外部方言,使其与0.6一起使用,因此此更改不会增加任何额外的困难。

  • 方言现在在初始连接上接收initialize()事件以确定连接属性。

  • 编译器生成的函数和运算符现在使用(几乎)形式为“visit_<opname>”和“visit_<funcname>fn”的常规调度函数来提供客户处理。这就取代了使用直接的访问者方法复制编译器子类中的“函数”和“运算符”字典的需要,并且还允许编译器子类在传入完整的函数或二进制表达式对象时完全控制呈现。

方言输入

方言的输入结构发生了变化。现在,每个方言通过以下方式导出其基本“方言”类以及该方言支持的完整SQL类型集: sqlalchemy.dialects.<name> . 例如,要导入一组pg类型:

from sqlalchemy.dialects.postgresql import INTEGER, BIGINT, SMALLINT,\
                                            VARCHAR, MACADDR, DATE, BYTEA

上面, INTEGER 实际上是平原吗 INTEGER 从类型 sqlalchemy.types 但pg方言使其可用的方式与pg特有的类型相同,例如 BYTEAMACADDR .

表达式语言更改

一种重要的表达语言

表达式语言有一个非常显著的行为变化,这可能会影响某些应用程序。python布尔表达式的布尔值,即 ==!= 与此类似,现在对要比较的两个子句对象进行精确的计算。

如我们所知,比较 ClauseElement 对任何其他对象返回另一个 ClauseElement

>>> from sqlalchemy.sql import column
>>> column('foo') == 5
<sqlalchemy.sql.expression._BinaryExpression object at 0x1252490>

这样,当转换为字符串时,python表达式将生成SQL表达式:

>>> str(column('foo') == 5)
'foo = :foo_1'

但是如果我们这么说会发生什么呢?

>>> if column('foo') == 5:
...     print("yes")
...

在以前的SQLAlchemy版本中,返回的 _BinaryExpression 是一个普通的python对象,其计算结果为 True . 现在它评估 ClauseElement 应该具有与要比较的哈希值相同的哈希值。含义:

>>> bool(column('foo') == 5)
False
>>> bool(column('foo') == column('foo'))
False
>>> c = column('foo')
>>> bool(c == c)
True
>>>

这意味着如下代码:

if expression:
    print("the expression is:", expression)

不会评估如果 expression 是一个二进制子句。由于不应使用上述模式,因此 ClauseElement 如果在布尔上下文中调用,则现在引发异常:

>>> bool(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
    raise TypeError("Boolean value of this clause is not defined")
TypeError: Boolean value of this clause is not defined

要检查是否存在 ClauseElement 表达式应该改为说:

if expression is not None:
    print("the expression is:", expression)

记住, 这也适用于表和列对象 .

变更的理由有两方面:

  • 表单的比较 if c1 == c2:  <do something> 现在可以写了

  • 支持正确散列 ClauseElement 对象现在在替代平台上工作,即Jython。到目前为止,sqlacalchemy在这方面严重依赖于cpython的特定行为(并且仍然偶尔遇到一些问题)。

更严格的“执行人”行为

SQLAlchemy中的“ExecuteMany”对应于对 execute() ,传递绑定参数集集合:

connection.execute(table.insert(), {'data':'row1'}, {'data':'row2'}, {'data':'row3'})

Connection 对象发送给 insert() 构造编译时,它将传递给编译器第一组绑定中存在的keynames,以确定语句的values子句的构造。熟悉此结构的用户将知道,其余字典中存在的其他键没有任何影响。现在不同的是,所有随后的字典至少需要包括 每一个 第一个字典中存在的键。这意味着这样的呼叫不再有效:

connection.execute(table.insert(),
                        {'timestamp':today, 'data':'row1'},
                        {'timestamp':today, 'data':'row2'},
                        {'data':'row3'})

因为第三行没有指定“timestamp”列。以前版本的SQLAlchemy只会为这些缺少的列插入空值。但是,如果 timestamp 上面示例中的列包含一个python端的默认值或函数,它将 not 被使用。这是因为“ExecuteMany”操作针对大量参数集的最大性能进行了优化,并且不会尝试评估缺少键的Python端默认值。因为默认值通常被实现为嵌入到insert语句中的SQL表达式,或者是基于insert字符串的结构再次触发的服务器端表达式,根据定义,这些表达式不能根据每个参数集有条件地触发,所以对于python端的默认值来说,它是不一致的。与SQL/Server端的默认值不同。(从0.5系列开始,基于SQL表达式的默认值被嵌入到内联中,以最小化大量参数集的影响)。

因此,SQLAlchemy 0.6通过禁止任何后续参数集保留任何字段为空来建立可预测的一致性。这样,就不会再出现python端默认值和函数的无声故障了,这些默认值和函数的行为与SQL和服务器端默认值保持一致。

union和其他“compound”构造一致地加括号

旨在帮助SQLite的规则已被删除,即另一个复合中的第一个复合元素的规则(如 union() 在一个 except_() )不会被括起来。这是不一致的,并且在PostgreSQL上产生错误的结果,PostgreSQL有关于交集的优先规则,这通常是令人惊讶的。在SQLite中使用复杂组合时,现在需要将第一个元素转换为子查询(这也与PG兼容)。结尾处的SQL表达式教程中提供了一个新示例 [https://www.sqlalchemy.org/docs/06/sqlexpression.html #unions-and-other-set-operations] 。看见 #1665 和r6690了解更多背景信息。

C结果提取扩展

这个 ResultProxy 为了提高性能,相关的元素(包括最常见的“行处理”函数,如unicode转换、数字/布尔转换和日期解析)被重新实现为可选的C扩展。这代表了SQLAlchemy通往“黑暗面”的道路的开始,我们希望通过在C中重新实现关键部分来继续提高性能。可以通过指定 --with-cextensions ,即 python setup.py --with- cextensions install .

扩展对使用direct获取结果的影响最大 ResultProxy 访问,即返回的 engine.execute()connection.execute()session.execute() . 在ORM返回的结果中 Query 对象,结果获取在开销中所占的百分比不高,因此ORM性能提高得比较温和,并且主要在获取大型结果集的领域中。性能的提高很大程度上取决于正在使用的DBAPI和用于访问每行列的语法(例如 row['name']row.name )当前的扩展不会影响插入/更新/删除的速度,也不会提高SQL执行的延迟,也就是说,一个花费大部分时间执行多个结果集非常小的语句的应用程序不会有太大的改进。

无论扩展如何,性能都在0.6和0.5之间得到了改善。快速了解使用sqlite连接和获取50000行的情况,主要使用直接sqlite访问 ResultProxy 和一个简单的映射ORM对象:

sqlite select/native: 0.260s

0.6 / C extension

sqlalchemy.sql select: 0.360s
sqlalchemy.orm fetch: 2.500s

0.6 / Pure Python

sqlalchemy.sql select: 0.600s
sqlalchemy.orm fetch: 3.000s

0.5 / Pure Python

sqlalchemy.sql select: 0.790s
sqlalchemy.orm fetch: 4.030s

上面,由于Python的性能增强,ORM获取行的速度比0.5快33%。通过C扩展,我们又得到了20%。然而, ResultProxy 使用C扩展的fetches比不使用C扩展的fetches提高了67%。其他测试报告说,对于某些场景,例如发生大量字符串转换的场景,速度提高了200%。

新架构功能

这个 sqlalchemy.schema 包裹已经收到一些长期需要的关注。最明显的变化是新扩展的DDL系统。在sqlAlchemy中,从0.5版开始,可以创建自定义DDL字符串并将其与表或元数据对象关联:

from sqlalchemy.schema import DDL

DDL('CREATE TRIGGER users_trigger ...').execute_at('after-create', metadata)

现在,完整的DDL结构套件在同一个系统下可用,包括用于创建表、添加约束等的结构套件:

from sqlalchemy.schema import Constraint, AddConstraint

AddContraint(CheckConstraint("value > 5")).execute_at('after-create', mytable)

此外,所有DDL对象现在都是常规的 ClauseElement 对象与任何其他SQLAlchemy表达式对象一样:

from sqlalchemy.schema import CreateTable

create = CreateTable(mytable)

# dumps the CREATE TABLE as a string
print(create)

# executes the CREATE TABLE statement
engine.execute(create)

并使用 sqlalchemy.ext.compiler 扩展您可以自己制作:

from sqlalchemy.schema import DDLElement
from sqlalchemy.ext.compiler import compiles

class AlterColumn(DDLElement):

    def __init__(self, column, cmd):
        self.column = column
        self.cmd = cmd

@compiles(AlterColumn)
def visit_alter_column(element, compiler, **kw):
    return "ALTER TABLE %s ALTER COLUMN %s %s ..." % (
        element.column.table.name,
        element.column.name,
        element.cmd
    )

engine.execute(AlterColumn(table.c.mycolumn, "SET DEFAULT 'test'"))

已弃用/删除的架构元素

模式包也被大大简化了。许多在整个0.5中被否决的选项和方法都已被删除。其他鲜为人知的访问器和方法也已被删除。

  • “owner”关键字参数从中移除 Table . 使用“schema”表示要在表名前面加上的任何命名空间。

  • 贬低 MetaData.connect()ThreadLocalMetaData.connect() 已删除-发送“bind”属性以绑定元数据。

  • 已弃用元数据。已删除table_iterator()方法(使用已排序的_tables)

  • “metadata”参数从中删除 DefaultGenerator 和子类,但仍存在于 Sequence ,这是DDL中的独立构造。

  • 贬低 PassiveDefault -使用 DefaultClause .

  • 已从中删除公共可变性 IndexConstraint 物体:

    • ForeignKeyConstraint.append_element()

    • Index.append_column()

    • UniqueConstraint.append_column()

    • PrimaryKeyConstraint.add()

    • PrimaryKeyConstraint.remove()

这些应该以声明的方式构造(即在一个构造中)。

  • 其他删除的内容:

    • Table.key (不知道这是干什么的)

    • Column.bind (通过column.table.bind获取)

    • Column.metadata (通过column.table.metadata获取)

    • Column.sequence (使用column.default)

其他行为变化

  • UniqueConstraintIndexPrimaryKeyConstraint 所有接受列名称或列对象列表作为参数。

  • 这个 use_alter 旗上 ForeignKey 现在是可以使用 DDL() 事件系统。这个重构的副作用是 ForeignKeyConstraint 对象与 use_alter=Truenot 在sqlite上发出,它不支持外键的alter。这对sqlite的行为没有影响,因为sqlite实际上不接受外键约束。

  • Table.primary_key is not assignable - use table.append_constraint(PrimaryKeyConstraint(...))

  • A Column 定义 ForeignKey 没有类型,例如 Column(name, ForeignKey(sometable.c.somecol)) 用于获取被引用列的类型。现在,对自动类型推断的支持是部分的,可能不适用于所有情况。

日志记录已打开

在这里和那里花费一些额外的方法调用,您可以在创建引擎、池或映射器之后设置信息和调试的日志级别,然后开始日志记录。这个 isEnabledFor(INFO) 方法现在按-connection``和调用 ``isEnabledFor(DEBUG) 如果在父连接上已启用,则为“resultproxy”。池日志记录发送到 log.info()log.debug() 不进行签入-请注意,池签出/签入通常每个事务一次。

反射/检查员API

反射系统,允许通过 Table('sometable', metadata, autoload=True) 已经开放到自己的细粒度API中,它允许直接检查数据库元素,如表、列、约束、索引等。此API将返回值表示为字符串、字典和 TypeEngine 物体。的内部 autoload=True 现在在这个系统的基础上,将原始数据库信息转换为 sqlalchemy.schema 构造是集中的,单个方言的契约大大简化,大大减少了跨不同后端的错误和不一致。

使用检查员:

from sqlalchemy.engine.reflection import Inspector
insp = Inspector.from_engine(my_engine)

print(insp.get_schema_names())

这个 from_engine() 在某些情况下,方法将为特定于后端的检查器提供附加功能,例如PostgreSQL提供 get_table_oid() 方法:

my_engine = create_engine('postgresql://...')
pg_insp = Inspector.from_engine(my_engine)

print(pg_insp.get_table_oid('my_table'))

返回支持

这个 insert()update()delete() 构造现在支持 returning() 方法,对应于PostgreSQL、Oracle、MS-SQL和FireBird支持的SQL返回子句。目前任何其他后端都不支持它。

以与 select() 构造,这些列的值将作为常规结果集返回:

result = connection.execute(
            table.insert().values(data='some data').returning(table.c.id, table.c.timestamp)
        )
row = result.first()
print("ID:", row['id'], "Timestamp:", row['timestamp'])

在Oracle需要复杂地使用输出参数(这些参数被重新路由到“模拟”结果集)的情况下,以及在MS-SQL使用笨拙的SQL语法的情况下,通过四个支持的后端返回的实现变化很大。退货的使用受到限制:

  • 它不适用于任何“executeMany()”类型的执行。这是所有受支持DBAPI的限制。

  • 某些后端(如Oracle)只支持返回单行的返回—这包括update和delete语句,这意味着update()或delete()构造必须只匹配单行,否则会引发错误(由Oracle而不是sqlachemy)。

返回也由sqlAlchemy自动使用,如果可用,并且没有由显式指定。 returning() 调用,获取单行insert语句新生成的主键值的值。这意味着对于需要主键值的insert语句,不再有“select nextval(sequence)”预执行。事实上,隐式返回特性确实比旧的“select nextval()”系统产生了更多的方法开销,后者使用快速而脏的cursor.execute()来获取序列值,并且在Oracle的情况下,需要额外的out参数绑定。因此,如果证明方法/协议开销比额外的数据库往返开销更昂贵,那么可以通过指定 implicit_returning=Falsecreate_engine() .

类型系统更改

新架构

类型系统已在幕后进行了彻底改造,以提供两个目标:

  • 将绑定参数和结果行值(通常是DBAPI要求)的处理与类型本身的SQL规范(数据库要求)分开。这与将数据库SQL行为与DBAPI分离的整体方言重构是一致的。

  • 为从 TypeEngine 对象和用于构造 TypeEngine 基于列反射的对象。

这些变化的重点包括:

  • 方言中的类型结构已经彻底改革。方言现在只将公开可用的类型定义为大写名称,并使用下划线标识符(即私有)定义内部实现类型。用SQL和DDL表示类型的系统已移到编译器系统中。这样一来,大多数方言中的类型对象就少得多。关于这个方言作者体系结构的详细文档位于 [source:/lib/sqlalc hemy/dialects/type_migration_guidelines.txt] .

  • 类型反射现在返回types.py中的准确大写类型,或者如果类型不是标准的SQL类型,则返回方言本身中的大写类型。这意味着反射现在返回关于反射类型的更准确的信息。

  • 子类的用户定义类型 TypeEngine 并希望提供 get_col_spec() 现在应该是子类 UserDefinedType .

  • 这个 result_processor() 所有类型类上的方法现在接受一个附加参数 coltype . 这是附加到cursor.description的DBAPI类型对象,在适用时应使用它来更好地决定应返回哪种类型的结果处理可调用。理想的结果是,处理器功能不需要使用 isinstance() 这是一个很昂贵的电话。

本机Unicode模式

随着更多DBAPI支持直接返回python-unicode对象,基础方言现在对第一个连接执行检查,该连接确定DBAPI是否返回python-unicode对象以进行varchar值的基本选择。如果是这样, String 类型和所有子类(即 TextUnicode 等)将在收到结果行时跳过“unicode”检查/转换步骤。这为大型结果集提供了显著的性能提升。“unicode模式”目前已知可用于:

  • sqlite3/pysqlite

  • psycopg2-sqla 0.6现在在每个psycopg2连接对象上默认使用“unicode”类型扩展

  • PG8000

  • cx_Oracle(我们使用输出处理器-很好的功能!)

其他类型可以根据需要选择禁用Unicode处理,例如 NVARCHAR 与MS-SQL一起使用时键入。

特别是,如果基于以前返回非Unicode字符串的DBAPI移植应用程序,“本机Unicode”模式具有明显不同的默认行为-列声明为 StringVARCHAR 现在,默认情况下返回Unicode,而它们之前会返回字符串。这可能会破坏需要非Unicode字符串的代码。可以通过传递来禁用psycopg2“本机Unicode”模式 use_native_unicode=Falsecreate_engine() .

对于显式不希望使用Unicode对象的字符串列,更通用的解决方案是使用 TypeDecorator 它将Unicode转换回UTF-8或任何需要的内容:

class UTF8Encoded(TypeDecorator):
    """Unicode type which coerces to utf-8."""

    impl = sa.VARCHAR

    def process_result_value(self, value, dialect):
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        return value

请注意, assert_unicode 标志现在已弃用。SQLAlChemy允许使用中的DBAPI和后端数据库在可用的情况下处理Unicode参数,并且不会通过检查传入类型来增加操作开销;如果传递无效数据,则SQLite和PostgreSQL等现代系统将在其一端引发编码错误。在SQLAlChemy确实需要将Python Unicode中的绑定参数强制转换为编码字符串的情况下,或者在显式使用Unicode类型时,如果对象是字节字符串,则会引发警告。使用过滤中记录的Python警告可以取消此警告或将其转换为异常,网址为:https://docs.python.org/library/warnings.html

通用枚举类型

我们现在有一个 Enumtypes 模块。这是一个字符串类型,它被赋予一个“标签”集合,该集合约束为这些标签提供的可能值。默认情况下,此类型生成 VARCHAR 使用最大标签的大小,并对CREATETABLE语句中的表应用CHECK约束。使用mysql时,类型默认使用mysql的枚举类型,使用postgresql时,类型将使用 CREATE TYPE <mytype> AS ENUM . 为了使用PostgreSQL创建类型,需要 name 必须为构造函数指定参数。类型还接受 native_enum=False 选项,它将为所有数据库发出varchar/check策略。请注意,PostgreSQL枚举类型当前不适用于PG8000或ZXJDBC。

反射返回方言特定类型

反射现在从数据库返回最具体的类型。也就是说,如果使用 String ,然后将其反射回来,反射列可能 VARCHAR . 对于支持更具体类型的方言,这就是您将得到的。所以A Text 类型将恢复为 oracle.CLOB 关于甲骨文 LargeBinary 可能是 mysql.MEDIUMBLOB 等等,这里的明显优势是反射可以尽可能多地保留数据库所说的信息。

一些处理大量表元数据的应用程序可能希望跨反射表和/或非反射表比较类型。有一个半私人的存取器 TypeEngine 打电话 _type_affinity 以及相关的比较助手 _compare_type_affinity . 此访问器返回“generic” types 类型对应的类:

>>> String(50)._compare_type_affinity(postgresql.VARCHAR(50))
True
>>> Integer()._compare_type_affinity(mysql.REAL)
False

其他API更改

通常的“通用”类型仍然是使用中的通用系统,即 StringFloatDateTime . 这里有一些变化:

  • 类型不再对默认参数进行任何猜测。特别地, NumericFloat 以及numeric、float和decimal子类,除非指定,否则不会生成任何长度或比例。这也继续包括有争议的 StringVARCHAR 类型(尽管当要求呈现不带长度的varchar时,mysql方言会先发制人)。不假定默认值,如果在create table语句中使用这些值,则如果基础数据库不允许这些类型的非长度版本,则会引发错误。

  • 这个 Binary 类型已重命名为 LargeBinary ,对于blob/bytea/类似类型。为了 BINARYVARBINARY ,这些直接呈现为 types.BINARYtypes.VARBINARY 以及在MySQL和MS-SQL方言中。

  • PickleType 当mutable=true时,现在使用==比较值,除非将带有比较函数的“comparator”参数指定给该类型。如果要清除自定义对象,则应实现 __eq__() 方法使基于值的比较准确。

  • numeric和float的默认“precision”和“scale”参数已被删除,现在默认为none。除非提供了这些值,否则默认情况下,numeric和float将不带数值参数呈现。

  • sqlite上的日期、时间和日期时间类型现在可以采用可选的“storage_format”和“regexp”参数。“存储格式”可用于使用自定义字符串格式存储这些类型。regexp“允许使用自定义正则表达式来匹配数据库中的字符串值。

  • __legacy_microseconds__ 关于SQLite TimeDateTime 不再支持类型。您应该使用新的“存储格式”参数。

  • DateTime SQLite上的类型现在默认使用更严格的正则表达式来匹配数据库中的字符串。如果使用的是以旧格式存储的数据,请使用新的“regexp”参数。

ORM变化

将ORM应用程序从0.5升级到0.6应该不需要更改,因为ORM的行为几乎相同。有一些默认参数和名称更改,一些加载行为也得到了改进。

新工作单位

工作单元的内部结构,主要是 topological.pyunitofwork.py ,已被完全重写并大大简化。这应该不会对使用产生影响,因为在刷新过程中所有现有的行为都得到了准确的维护(或者至少,只要它是由我们的测试套件和少数对其进行了大量测试的生产环境所执行的)。flush()的性能现在使用了20-30%的方法调用,并且应该使用更少的内存。源代码的意图和流程现在应该相当容易理解,而flush的架构在这一点上是相当开放的,为潜在的复杂新领域创造了空间。刷新过程不再依赖递归,因此可以刷新任意大小和复杂性的刷新计划。此外,映射器的“save”进程(它发出insert和update语句)现在缓存这两个语句的“compiled”形式,以便通过非常大的刷新进一步显著减少调用计数。

与早期版本的0.6或0.5相比,使用flush观察到的任何行为变化都应尽快报告给我们-我们将确保不会丢失任何功能。

改变到 query.update()query.delete()

  • query.update()上的“expire”选项已重命名为“fetch”,从而与query.delete()的选项匹配。

  • query.update()query.delete() 对于同步策略,两者都默认为“评估”。

  • update()和delete()的“同步”策略在失败时引发错误。“fetch”没有隐式回退。评估的失败是基于标准的结构,所以成功/失败是基于代码结构的确定性。

relation() is officially named relationship()

这是为了解决长期存在的问题,“关系”在关系代数术语中表示“表或派生表”。这个 relation() 名称,这是较少的输入,将在可预见的未来,所以这一变化应该是完全无痛的。

子查询预加载

增加了一种新的预加载方式,称为“子查询”加载。这是在第一个查询之后立即发出第二个SQL查询的负载,它为第一个查询中的所有父级加载完整的集合,并使用内部联接向上联接到父级。子查询加载的使用方式与当前联接的抢先加载类似,使用 `subqueryload() 和 ````subqueryload_all() 选项以及 ````lazy='subquery' “设置” ``relationship() '.子查询加载对于加载许多更大的集合通常效率更高,因为它无条件地使用内部联接,并且也不重新加载父行。

`eagerload()``, ``eagerload_all()`` is now ``joinedload()``, ``joinedload_all()`

为了为新的子查询加载功能腾出空间,现有的 `eagerload()``/``eagerload_all() 选项现在被取代 ````joinedload() 和 ````joinedload_all() ''.在可预见的未来,旧名字会像 ``relation() '.

`lazy=False|None|True|'dynamic'`` now accepts ``lazy='noload'|'joined'|'subquery'|'select'|'dynamic'`

继续以装载机策略为主题展开,标准关键词为 `lazy “选项” ``relationship() 现在是 ``select 对于延迟加载(通过在属性访问时发出的选择), ````joined 对于加入的热切装载, ````subquery 对于子查询预加载, ````noload 因为不应发生加载,以及 ````dynamic 对于“动态”关系。老年人 ````True '' ``False '' ``None `参数仍被接受,其行为与以前相同。

innerjoin=关系为真,joinedLoad

现在可以指示已连接的、热切加载的scalar和集合使用内部连接而不是外部连接。在PostgreSQL上,可以观察到这在某些查询上提供了300-600%的加速。将任意多个的此标志设置为不可为空的外键上的多个标志,同样,也为保证存在相关项的任何集合设置此标志。

在映射器级别:

mapper(Child, child)
mapper(Parent, parent, properties={
    'child':relationship(Child, lazy='joined', innerjoin=True)
})

在查询时间级别:

session.query(Parent).options(joinedload(Parent.child, innerjoin=True)).all()

这个 innerjoin=True 旗在 relationship() 级别也将对任何 joinedload() 不重写值的选项。

多对一增强

  • 现在,许多对一的关系在少数情况下会解雇一个拉西洛人,包括在大多数情况下,当一个新的拉西洛人被替换时,他们将得不到“旧”的价值。

  • 与联接表子类的多对一关系现在使用get()进行简单加载(称为“use_get”条件),即 Related ->`` sub(base)``,不需要根据基表重新定义primaryjoin条件。 [门票:1186]

  • 使用声明性列指定外键,即 ForeignKey(MyRelatedClass.id) 不会破坏“使用-获取”条件 [门票:1492]

  • relationship()、joinedLoad()和joinedLoad_all()现在具有一个名为“innerjoin”的选项。指定 TrueFalse 控制一个热切连接是构造为内部连接还是外部连接。默认是 False 一如既往。映射器选项将覆盖关系()上指定的任何设置。通常应为多对一、不可为空的外键关系设置,以允许改进联接性能。 [门票:1544]

  • 当存在限制/偏移量时,联接的预加载的行为使主查询被包装在子查询中,现在,当所有的预加载都是多对一联接时,这种行为会成为例外。在这些情况下,由于多对一联接不会向结果中添加行,因此在不增加子查询额外开销的情况下,热切联接直接针对父表以及限制/偏移量。

    例如,在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

具有联接表继承的可变主键

一个联合表继承配置,其中子表有一个pk,父pk的外键现在可以在支持级联的数据库(如postgresql)上更新。 mapper() 现在有一个选择 passive_updates=True 表示该外键自动更新。如果在非级联数据库(如sqlite或mysql/myisam)上,将此标志设置为 False . 未来的功能增强将尝试根据使用中的方言/表格样式自动配置此标志。

烧杯缓存

烧杯集成的一个有希望的新例子是 examples/beaker_caching . 这是一个简单的方法,它将烧杯缓存应用于 Query . 缓存参数通过提供 query.options() ,并允许对缓存的内容进行完全控制。SQLAlchemy 0.6包括对 Session.merge() 方法来支持这个和类似的配方,并在大多数场景中提供显著改进的性能。

其他变化

  • 返回的“row tuple”对象 Query 当选择多个列/实体时,现在可以选择并执行更高的操作。

  • query.join() 重新设计以提供更一致的行为和更大的灵活性(包括 [门票:1537] )

  • query.select_from() 接受多个子句以在FROM子句中生成多个逗号分隔的条目。从多个homed join()子句中选择时很有用。

  • “不加载=真”标志打开 Session.merge() 已弃用,现在为“load=false”。

  • 添加了“make_transient()”helper函数,该函数将持久/分离的实例转换为临时实例(即删除实例_键并从任何会话中删除)。 [门票:1052]

  • mapper()上的allow_null_pks标志已被弃用,并已重命名为allow_partial_pks。默认情况下,它处于“打开”状态。这意味着对于任何主键列都具有非空值的行将被视为标识。这种情况的需要通常只在映射到外部联接时发生。当设置为false时,其中包含空值的pk将不会被视为主键-特别是这意味着结果行将返回为无(或未填充到集合中),0.6中的new还表示session.merge()不会为此类pk值向数据库发出往返访问。 [门票:1680]

  • “backref”的机制已经完全合并到细粒度的“back-populates”系统中,并且完全发生在 _generate_backref() 方法 RelationProperty . 这使得初始化过程 RelationProperty 更简单,允许更容易地传播设置(例如从 RelationProperty )反向引用。内部 BackRef() 消失了 backref() 返回一个可以被 RelationProperty .

  • 的键属性 ResultProxy 现在是一个方法,因此引用它 (result.keys )必须更改为方法调用 (result.keys()

  • ResultProxy.last_inserted_ids 现在已弃用,请使用 ResultProxy.inserted_primary_key 相反。

已弃用/已删除的ORM元素

在整个0.5中被否决并引发了拒绝警告的大多数元素都已被删除(少数例外)。所有标记为“待定取消预测”的元素现在都将被弃用,并在使用时发出警告。

  • 已删除sessionmaker()和其他服务器上的“transactional”标志。使用“autocommit=true”指示“transactional=false”。

  • 已删除mapper()上的“多态提取”参数。可以使用“with_多态性”选项控制加载。

  • 已删除mapper()上的“select_table”参数。对于此功能,请使用“with_多态性=”“*”,<some selectable>)。

  • 同义词()上的“proxy”参数已删除。由于“代理生成”行为现在是自动的,因此此标志在整个0.5过程中都不起作用。

  • 将单个元素列表传递给joinedLoad()、joinedLoad_all(),它包含_eager()、lazyload()、defer()和undefer(),而不是多个位置 * 参数已弃用。

  • 将单个元素列表传递给query.order_by()、query.group_by()、query.join()或query.outerjoin(),而不是多个位置 * 参数已弃用。

  • query.iterate_instances() 被移除。使用 query.instances() .

  • Query.query_from_parent() 被移除。使用sqlachemy.orm.with_parent()函数生成“parent”子句,或者 query.with_parent() .

  • query._from_self() 删除,使用 query.from_self() 相反。

  • composite()的“comparator”参数被删除。使用“比较器工厂”。

  • RelationProperty._get_join() 被移除。

  • 会话上的“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 仍处于弃用状态。请参阅https://www.sqlalchemy.org/trac/wiki/Usag电子处方/SessionAwareMapper上的用法食谱

  • 路过 InstanceState (内部sqlAlchemy状态对象)到 attributes.init_collection()attributes.get_history() 已弃用。这些函数是公共API,通常需要一个常规的映射对象实例。

  • “engine”参数 declarative_base() 被移除。使用“bind”关键字参数。

扩展

SQLSoup

SQLSoup已经过现代化和更新,以反映常见的0.5/0.6功能,包括定义良好的会话集成。请阅读新文档,网址为 [https://www.sqlalc hemy.org/docs/06/reference/ext/sqlsoup.html] 。

声明的

这个 DeclarativeMeta (默认元类为 declarative_base )之前允许子类修改 dict_ 若要添加类属性(例如列),请执行以下操作。这不再起作用了, DeclarativeMeta 构造函数现在忽略 dict_ 。相反,应该直接分配类属性,例如 cls.id=Column(...) ,或 MixIn class 应该使用方法而不是元类方法。