迁移到SQLAlchemy 2.0

2021-02-19 01:58:29

对于核心和ORM组件中的各种keySQLAlchemy使用模式,SQLAlchemy 2.0提出了重大转变。此发行版的目的是对自SQLAlchemy诞生以来的一些最基本的假设进行些微调整,并提供一种新的简化的使用模型,该模型希望在Core和ORM组件之间更加简约和一致,并且功能更强大。 。 Python成为仅Python 3的转变以及适用于Python 3的渐进式打字系统的出现是这种转变的最初灵感,Python社区不断变化的性质也是如此,如今该社区不仅包括核心数据库程序员,而且还包括新的数据科学家社区以及许多不同学科的学生。

SQLAlchemy从Python 2.3开始,它没有上下文管理器,没有函数装饰器,没有Unicode作为第二类功能以及许多其他缺点,这些缺点现在都是未知的。 SQLAlchemy 2.0的最大变化是针对SQLAlchemy早期开发过程中遗留下来的剩余假设,以及增量引入关键的API功能(例如Query和Declarative)所产生的剩余工件。它还希望标准化一些已被证明非常有效的新功能。

SQLAlchemy 2.0过渡作为一系列步骤出现在SQLAlchemy 1.4版本中,这些步骤允许使用逐步的迭代过程将任何大小或复杂度的应用程序迁移到SQLAlchemy 2.0。从Python 2到Python 3过渡的经验教训启发了一个系统,该系统希望在最大程度上不要求任何“重大”更改,也不需要任何通用或根本不需要进行的更改。

为了证明2.0体系结构并允许完全迭代的过渡环境,在1.4系列中提供了2.0的所有新API和功能的全部范围;这包括主要的新功能领域,例如SQL缓存系统,新的ORM语句执行模型,针对ORM和Core的新事务范例,用于统一经典和声明性映射的新ORM声明系统,对Python数据类的支持以及对Core和Java的异步支持。 ORM。

以下小节中介绍了实现2.0迁移的步骤。总的来说,一般的策略是,一旦应用程序在1.4 all上运行且打开了所有警告标志并且不发出任何2.0弃用警告,则该应用程序现在与SQLAlchemy 2.0相互兼容。

在典型的非平凡应用程序的情况下,第一步是将现有应用程序升级到1.4,以确保其在SQLAlchemy 1.3上运行且没有弃用警告。版本1.4确实有一些与先前版本中警告的条件相关的更改,包括在1.3中引入的一些警告,尤其是对Relationship.viewonly和Relationship.sync_backref标志的行为的某些更改。

为了获得最佳结果,应用程序应该能够运行或通过其最新的SQLAlchemy 1.3版本,并且没有SQLAlchemy弃用警告。这些是针对SADeprecationWarning类发出的警告。

一旦应用程序适合使用SQLAlchemy 1.3,下一步就是让它在SQLAlchemy 1.4上运行。在绝大多数情况下,从SQLAlchemy 1.3到1.4,应用程序都应该运行没有问题。但是,无论是在1.x版本还是1.y版本之间,情况总是如此,API和行为已经发生了细微的变化,或者在某些情况下发生了一些细微的变化,并且SQLAlchemyproject在最初的几个月中总是获得大量的回归报告。

1.x-> 1.y的发布过程通常在边距周围有一些变化,这些变化稍微有些戏剧性,并且基于用例,这些用例被认为很少使用。对于1.4,标识为该领域的更改如下:

现在,URL对象是不可变的-这会影响将要处理URL对象的代码,并且可能会影响使用CreateEnginePlugin扩展点的代码。这是一种罕见的情况,但是可能会特别影响某些使用特殊数据库供应逻辑的测试套件。 github搜索使用相对较新且鲜为人知的CreateEnginePlugin类的代码,发现两个项目不受更改的影响。

不再将SELECT语句隐式视为FROM子句-此更改可能会影响代码,该代码某种程度上依赖于Select构造中大部分无法使用的行为,它将创建未命名的子查询,这些子查询通常令人困惑且无法正常工作。在任何情况下,这些子查询都会被大多数数据库拒绝,因为通常需要一个名称(SQLite除外),但是某些应用程序可能需要调整一些不经意依赖此查询的查询。

select()。join()和outerjoin()向当前查询添加JOIN条件,而不是创建子查询-有点相关的Select类具有特色的.join()和.outerjoin()方法,它们隐式创建了一个子查询,然后返回一个Join构造,这将再次变得毫无用处,并引起很多混乱。决定做出决定,采用更加有用的2.0样式的建立连接方法,其中这些方法现在的工作方式与ORM Query.join()方法相同。

现在许多Core和ORM语句对象在编译阶段执行其大部分构造和验证-与Que​​ry或Select构造有关的一些错误消息可能要等到编译/执行时才发出,而不是在构造时发出,这可能会影响某些测试套件,正在针对故障模式进行测试。

有关SQLAlchemy 1.4更改的完整概述,请参见SQLAlchemy 1.4的新增功能?文档。

Warning: Can only detect less than 5000 characters

根据以上指导,我们可以将程序迁移为使用2.0样式,此外,我们的程序更加清晰:

从sqlalchemy导入列从sqlalchemy导入从sqlalchemy导入create_engine从sqlalchemy导入表从sqlalchemy导入文本引擎= create_engine(" sqlite://")#不要依赖于DML和DDL的自动提交与引擎。 begin()作为连接:#使用connection.execute(),而不是engine.execute()#使用text()构造执行文本SQL连接。执行(text(" CREATE TABLE foo(id integer)"))连接。执行(text(" INSERT INTO foo(id)VALUES(1)"))foo = table(" foo",column(" id"))引擎 。 connect()作为连接:#使用connection.execute(),而不是engine.execute()#select()现在在位置上接受列/表表达式result = connection。执行(选择(foo.c.id))打印(结果.fetchall())

“ 2.0弃用模式”的目标是,运行不带RemovedIn20Warning警告且打开“ 2.0弃用模式”的程序即可在SQLAlchemy 2.0中运行。

可以迭代开发代码来解决这些警告。在SQLAlchemy项目本身中,采用的方法如下:

在测试套件中启用SQLALCHEMY_WARN_20 = 1环境变量,对于SQLAlchemy,这在tox.ini文件中

在测试套件的设置中,设置一系列警告过滤器,这些过滤器将为特定的警告子集选择引发异常或被忽略(或记录)的警告。一次仅处理一个警告子组。下面,为需要更改核心级别.execute()调用以使所有测试通过的应用程序配置了警告过滤器,但所有其他2.0样式的警告将被禁止:

从sqlalchemy导入警告import exc#用于与execute()/ scalar()相关的警告,在[r" [?: Executable | Engine)\。(?: execute | scalar)\(\)中提高msg。功能" ,r"使用隐式"自动提交当前语句。 "自动提交," ,r" SQLAlchemy 2.0中的connection.execute \(\)方法将接受" "参数作为单个字典或"的单个序列"仅字典。" ,r" Connection.connect \(\)函数/方法被认为是旧有的" ,r"。* DefaultGenerator.execute \(\)" ,]:警告。对于所有其他警告,filterwarnings(" error&#34 ;,消息= msg,类别=sa_exc。RemovedIn20Warning,)#仅记录警告。 filterwarnings(" always&#34 ;,类别=排除。RemovedIn20Warning)

当在应用程序中解决了警告的每个子类别时,可以将“始终”过滤器捕获的新警告添加到要解决的“错误”列表中。

Engine对象在2.0版中具有更新的事务级别API。在1.4中,可以通过将标记future = True传递给create_engine()函数来使用此新API。

当使用create_engine.future标志时,Engineand Connection对象完全支持2.0 API,而不完全支持任何旧版功能,包括Connection.execute()的新参数格式,删除了“隐式自动提交”,字符串语句需要文本()构造,除非使用Connection.exec_driver_sql()方法,并且删除了Engine中的无连接执行。

如果已解决有关使用引擎和连接的所有RemovedIn20Warning警告,则可以启用create_engine.future标志,并且不会出现任何错误。

在Engine处描述了新引擎,该引擎提供了一个新的Connection对象。除了上述更改之外,Connection对象还具有Connection.commit()和Connection.rollback()方法,以支持新的“随行提交”操作模式:

从sqlalchemy中导入带有engine的create_engine engine = create_engine(" postgresql:///")。 connect()as conn:conn。执行(文本("插入表(x)值(:some_x)"),{" some_x":10})conn。 commit()#随手提交

会话对象还具有2.0版中更新的事务/连接级别API。在1.4中,可以使用Session或sessionmaker上的Session.future标志使用此API。

会话在解析要用于连接的引擎时不再支持“绑定元数据”。这意味着必须将Engine对象传递给构造函数(这可以是旧式或将来样式的对象)。

Session.commit()方法始终向数据库发出COMMIT,而不是尝试协调“子事务”。

Session.rollback()方法始终总是回滚整个事务堆栈,而不是尝试将“子事务”保持在原位。

会话还支持1.4中更灵活的创建模式,这些模式现在与Connection对象使用的模式紧密匹配。重点包括该会话可用作上下文管理器:

此外,sessionmaker对象还支持sessionmaker.begin()上下文管理器,该上下文管理器将创建一个Session并在一个块中开始/提交事务:

有关会话创建模式与连接创建模式的比较,请参见会话级别与引擎级别的事务控制部分。

一旦应用程序通过所有测试/以SQLALCHEMY_WARN_20 = 1运行并且所有exc.RemovedIn20Warning事件被设置为引发错误,应用程序就准备就绪!

以下各节将详细说明对所有主要API修改进行的特定更改。

在SQLAlchemy 1.x中,以下语句将自动提交基础的DBAPI事务,但是在SQLAlchemy2.0中不会发生:

自定义DML需要提交的常见解决方法,即“ autocommit”执行选项将被删除:

与1.x样式和2.0style执行交叉兼容的方法是利用Connection.begin()方法或Engine.begin()上下文管理器:

与引擎。 begin()as conn:conn。执行(some_table。insert()。值(foo =' bar'))conn。用engine执行(some_other_table。insert()。values(bat =' hoho'))。 connect()as conn:与conn一起。 begin():conn。执行(some_table。insert()。值(foo =' bar'))conn。用engine执行(some_other_table。insert()。values(bat =' hoho'))。 begin()as conn:conn。执行(文字(" EXEC my_procedural_thing()"))

当将2.0样式与create_engine.futureflag一起使用时,也可以使用“随行提交”样式,因为Connection功能具有自动开始行为,这种行为在没有显式调用Connection.begin()的情况下首次调用语句时发生。 :

启用2.0弃用模式时,发生不赞成使用的“自动提交”功能时将发出警告,指示应注意显式事务的位置。

SQLAlchemy的第一个版本与Python DBAPI(PEP 249)的精神背道而驰,因为它试图掩盖PEP 249对事务的“隐式开始”和“显式提交”的强调。十五年后,我们现在发现这本质上是一个错误,因为SQLAlchemy尝试“隐藏”事务存在的许多模式导致了更为复杂的API,该API工作不一致,并且特别使那些对关系数据库和ACID事务不熟悉的用户特别困惑。一般来说。 SQLAlchemy 2.0将取消所有隐式提交事务的尝试,使用模式将始终要求用户以某种方式来区分事务的“开始”和“结束”,就像在Python中读取或写入文件一样。 “开始”和“结束”。

在自动提交纯文本语句的情况下,实际上存在分析每个语句以检测自动提交的规则表达式!不足为奇的是,此regex一直无法容纳各种类型的语句和存储过程,这意味着“写”到数据库,导致持续的混乱,因为某些语句在数据库中产生结果,而另一些则不会。通过阻止用户意识到事务性概念,我们得到了很多有关此问题的错误报告,因为用户不了解数据库总是使用事务,而不管某个层是否在自动提交事务。

SQLAlchemy 2.0将要求

......