SqlAlchemy 2.0 中文文档(五十五)(5)https://developer.aliyun.com/article/1563192
对象无法转换为‘持久’状态,因为此标识映射不再有效。
自版本 1.4.26 新增。
添加此消息是为了适应以下情况:当迭代一个在原始Session
关闭后或在其上调用Session.expunge_all()
方法后仍会产生 ORM 对象的Result
对象时。当一个Session
一次性删除所有对象时,该Session
使用的内部标识映射将被替换为新的,并且原始映射将被丢弃。一个未使用且未缓冲的Result
对象将在内部维护对该现在被丢弃的标识映射的引用。因此,当消耗了Result
时,将要产生的对象无法与该Session
关联。这种安排是有意设计的,因为通常不建议在创建它的事务上下文之外迭代未缓冲的Result
对象:
# context manager creates new Session with Session(engine) as session_obj: result = sess.execute(select(User).where(User.id == 7)) # context manager is closed, so session_obj above is closed, identity # map is replaced # iterating the result object can't associate the object with the # Session, raises this error. user = result.first()
使用 asyncio
ORM 扩展时,通常不会出现上述情况,因为当 AsyncSession
返回一个同步风格的 Result
时,结果在语句执行时已经被预先缓冲。这样做是为了允许次级急切加载器在不需要额外的 await
调用的情况下调用。
若要在上述情况下像 asyncio
扩展一样预先缓冲结果,可以使用 prebuffer_rows
执行选项如下所示:
# context manager creates new Session with Session(engine) as session_obj: # result internally pre-fetches all objects result = sess.execute( select(User).where(User.id == 7), execution_options={"prebuffer_rows": True} ) # context manager is closed, so session_obj above is closed, identity # map is replaced # pre-buffered objects are returned user = result.first() # however they are detached from the session, which has been closed assert inspect(user).detached assert inspect(user).session is None
在上面的例子中,所选的 ORM 对象完全在 session_obj
块内生成,与 session_obj
关联并在 Result
对象内缓冲以供迭代。在块外,session_obj
被关闭并清除这些 ORM 对象。迭代 Result
对象将产生这些 ORM 对象,但是由于它们的来源 Session
已将它们清除,它们将以 分离 状态传递。
注意
上文提到的“预缓冲”与“未缓冲”的 Result
对象指的是 ORM 将传入的原始数据库行从 DBAPI 转换为 ORM 对象的过程。这并不意味着底层的 cursor
对象本身,它代表了来自 DBAPI 的待处理结果,是缓冲的还是非缓冲的,因为这本质上是一个更低层次的缓冲。有关 cursor
结果本身的缓冲背景,请参阅 使用服务器端游标 (即流式结果) 部分。
无法解释注释的声明式表格形式的类型注释
SQLAlchemy 2.0 引入了一个新的 注释式声明表 声明系统,它会在运行时从类定义中的 PEP 484 注释中派生 ORM 映射属性信息。这种形式的要求是,所有 ORM 注释都必须使用一个名为Mapped
的通用容器才能正确注释。包含显式 PEP 484 类型注释的传统 SQLAlchemy 映射,例如那些使用 传统 Mypy 扩展 进行类型支持的映射,可能包含不包括此通用容器的诸如relationship()
之类的指令。
要解决此问题,可以将类标记为__allow_unmapped__
布尔属性,直到它们可以完全迁移到 2.0 语法。请参阅迁移到 2.0 第六步 - 向显式类型化的 ORM 模型添加 allow_unmapped 的迁移说明以获取示例。
另请参阅
迁移到 2.0 第六步 - 向显式类型化的 ORM 模型添加 allow_unmapped - 在 SQLAlchemy 2.0 - 主要迁移指南 文档中
当将转换为数据类时,属性源自于不是数据类的超类。
当与任何不是自身声明为数据类的混入类或抽象基类一起使用 SQLAlchemy ORM 映射数据类功能时,会出现此警告,例如下面的示例:
from __future__ import annotations import inspect from typing import Optional from uuid import uuid4 from sqlalchemy import String from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import Mapped from sqlalchemy.orm import mapped_column from sqlalchemy.orm import MappedAsDataclass class Mixin: create_user: Mapped[int] = mapped_column() update_user: Mapped[Optional[int]] = mapped_column(default=None, init=False) class Base(DeclarativeBase, MappedAsDataclass): pass class User(Base, Mixin): __tablename__ = "sys_user" uid: Mapped[str] = mapped_column( String(50), init=False, default_factory=uuid4, primary_key=True ) username: Mapped[str] = mapped_column() email: Mapped[str] = mapped_column()
在上述情况下,由于Mixin
本身不是扩展自MappedAsDataclass
,因此会生成以下警告:
SADeprecationWarning: When transforming <class '__main__.User'> to a dataclass, attribute(s) "create_user", "update_user" originates from superclass <class '__main__.Mixin'>, which is not a dataclass. This usage is deprecated and will raise an error in SQLAlchemy 2.1\. When declaring SQLAlchemy Declarative Dataclasses, ensure that all mixin classes and other superclasses which include attributes are also a subclass of MappedAsDataclass.
修复方法是在Mixin
的签名中也添加MappedAsDataclass
:
class Mixin(MappedAsDataclass): create_user: Mapped[int] = mapped_column() update_user: Mapped[Optional[int]] = mapped_column(default=None, init=False)
Python 的 PEP 681 规范不包含不是数据类本身的数据类超类上声明的属性; 根据 Python 数据类的行为,这些字段会被忽略,如下例所示:
from dataclasses import dataclass from dataclasses import field import inspect from typing import Optional from uuid import uuid4 class Mixin: create_user: int update_user: Optional[int] = field(default=None) @dataclass class User(Mixin): uid: str = field(init=False, default_factory=lambda: str(uuid4())) username: str password: str email: str
在上述情况下,User
类将不会在其构造函数中包含create_user
,也不会尝试将update_user
解释为数据类属性。这是因为Mixin
不是数据类。
SQLAlchemy 2.0 系列中的数据类功能未正确遵守此行为;相反,非数据类混合类和超类上的属性被视为最终数据类配置的一部分。但是像 Pyright 和 Mypy 这样的类型检查器不会将这些字段视为数据类构造函数的一部分,因为根据PEP 681,它们应该被忽略。由于否则它们的存在是模棱两可的,因此 SQLAlchemy 2.1 将要求在数据类层次结构中具有 SQLAlchemy 映射属性的混合类本身必须是数据类。
创建类时遇到的 Python 数据类错误
当使用MappedAsDataclass
混合类或registry.mapped_as_dataclass()
装饰器时,SQLAlchemy 利用 Python 标准库中实际的Python 数据类模块,以将数据类行为应用于目标类。此 API 具有自己的错误场景,其中大多数涉及在用户定义的类上构建__init__()
方法;在类上声明的属性的顺序,以及在超类上声明的属性,决定了__init__()
方法将如何构建,并且有特定规则规定了属性的组织方式以及它们应如何使用参数,如init=False
,kw_only=True
等。SQLAlchemy 不控制或实现这些规则。因此,对于这种类型的错误,请参阅Python 数据类文档,特别注意应用于继承的规则。
另请参阅
声明式数据类映射 - SQLAlchemy 数据类文档
Python 数据类 - 在 python.org 网站上
继承 - 在 python.org 网站上
按主键进行每行 ORM 批量更新要求记录包含主键值
当在给定记录中使用 ORM 按主键批量更新功能而未提供主键值时,将出现此错误,例如:
>>> session.execute( ... update(User).where(User.name == bindparam("u_name")), ... [ ... {"u_name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"u_name": "patrick", "fullname": "Patrick Star"}, ... ], ... )
上述,将参数字典列表与使用Session
执行 ORM 启用的 UPDATE 语句结合使用将自动使用按主键进行 ORM 批量更新,该功能期望参数字典包含主键值,例如:
>>> session.execute( ... update(User), ... [ ... {"id": 1, "fullname": "Spongebob Squarepants"}, ... {"id": 3, "fullname": "Patrick Star"}, ... {"id": 5, "fullname": "Eugene H. Krabs"}, ... ], ... )
若要在不提供每条记录主键值的情况下调用 UPDATE 语句,请使用 Session.connection()
获取当前的 Connection
,然后使用它进行调用:
>>> session.connection().execute( ... update(User).where(User.name == bindparam("u_name")), ... [ ... {"u_name": "spongebob", "fullname": "Spongebob Squarepants"}, ... {"u_name": "patrick", "fullname": "Patrick Star"}, ... ], ... )
另请参阅
按主键进行 ORM 批量更新
禁用批量 ORM 按主键更新,以及包含多个参数集的 UPDATE 语句
AsyncIO 异常
等待所需
SQLAlchemy 异步模式要求使用异步驱动程序连接到数据库。尝试使用不兼容的 DBAPI 的异步版本与 SQLAlchemy 的异步版本一起使用时通常会引发此错误。
另请参阅
异步 I/O (asyncio) ### 缺少 Greenlet
在没有创建 SQLAlchemy AsyncIO 代理类设置的协程生成上下文之外启动异步 DBAPI 调用时,通常会引发此错误。通常情况下,当在意外位置尝试进行 IO 操作时,使用了不直接提供 await
关键字的调用模式时会发生此错误。在使用 ORM 时,几乎总是由于使用了延迟加载,这在 asyncio 中不直接支持,需要采取额外步骤和/或替代加载器模式才能成功使用。
另请参阅
使用 AsyncSession 时防止隐式 IO - 涵盖了大多数可能发生此问题的 ORM 方案以及如何缓解这个问题,包括在懒加载情况下使用的特定模式。 ### 无可用检查
当前不支持直接在 AsyncConnection
或 AsyncEngine
对象上直接使用 inspect()
函数,因为尚未提供 Inspector
对象的可等待形式。相反,该对象是通过使用 inspect()
函数获取的,以一种方式,使其引用 AsyncConnection
对象的底层 AsyncConnection.sync_connection
属性;然后,通过使用 AsyncConnection.run_sync()
方法以及执行所需操作的自定义函数,以“同步”调用样式使用 Inspector
:
async def async_main(): async with engine.connect() as conn: tables = await conn.run_sync( lambda sync_conn: inspect(sync_conn).get_table_names() )
另请参阅
使用 Inspector 检查模式对象 - 使用 asyncio 扩展的 inspect()
的附加示例。### 必须等待
SQLAlchemy 的异步模式需要使用异步驱动程序连接到数据库。当尝试使用不兼容的 DBAPI 时,通常会引发此错误。
另请参阅
异步 I/O (asyncio)
缺少 Greenlet
尝试在由 SQLAlchemy AsyncIO 代理类设置的 greenlet spawn 上下文之外启动异步 DBAPI 调用时会引发此错误。通常,当在意外位置尝试进行 IO 操作时,使用不直接提供 await
关键字的调用模式会发生此错误。在使用 ORM 时,这几乎总是由于使用 懒加载,在 asyncio 中,需要通过额外的步骤和/或替代加载程序模式才能成功使用。
另请参阅
在使用 AsyncSession 时预防隐式 IO - 涵盖了大多数可能出现此问题的 ORM 方案以及如何缓解,包括在懒加载场景中使用的特定模式。
无可用检查
直接在 AsyncConnection
或 AsyncEngine
对象上使用 inspect()
函数目前不受支持,因为尚未提供 Inspector
对象的可等待形式。相反,通过以获取 AsyncConnection
对象的基础 AsyncConnection.sync_connection
属性的方式获取该对象;然后使用 Inspector
通过使用 AsyncConnection.run_sync()
方法以及执行所需操作的自定义函数来进行 “同步” 调用:
async def async_main(): async with engine.connect() as conn: tables = await conn.run_sync( lambda sync_conn: inspect(sync_conn).get_table_names() )
另请参阅
使用 Inspector 检查模式对象 - 使用 asyncio 扩展与 inspect()
的其他示例。
核心异常类
查看 核心异常 以获取核心异常类。
ORM 异常类
查看 ORM 异常 以获取 ORM 异常类。
旧版本异常
本节中的异常不是由当前的 SQLAlchemy 版本生成的,但提供了这些异常以适应异常消息的超链接。
在 SQLAlchemy 2.0 中,<某个函数> 将不再 <某事>
SQLAlchemy 2.0 对于核心和 ORM 组件中的许多关键 SQLAlchemy 使用模式都表示了一个重大转变。2.0 发布的目标是在 SQLAlchemy 自早期开始以来的一些最基本的假设中进行轻微调整,并提供一个新的简化使用模型,希望它在核心和 ORM 组件之间更加简约一致,并更加强大。
在 SQLAlchemy 2.0 - 主要迁移指南中介绍的 SQLAlchemy 2.0 项目包含了一个全面的未来兼容系统,该系统已集成到 SQLAlchemy 1.4 系列中,因此应用程序将具有明确、无歧义和逐步的升级路径,以将应用程序迁移到完全兼容 2.0 的状态。RemovedIn20Warning
废弃警告是该系统的基础,提供了关于现有代码库中需要修改的行为的指导。如何启用此警告的概述在 SQLAlchemy 2.0 Deprecations Mode 中。
另请参阅
SQLAlchemy 2.0 - 主要迁移指南 - 从 1.x 系列升级过程的概述,以及 SQLAlchemy 2.0 的当前目标和进展。
SQLAlchemy 2.0 Deprecations Mode - 关于如何在 SQLAlchemy 1.4 中使用“2.0 废弃模式”的具体指南。### 对象正在被合并到会话中,沿着反向引用级联。
此消息指的是 SQLAlchemy 的“backref cascade”行为,在版本 2.0 中已删除。这指的是将对象添加到Session
中,因为该会话中已经存在的另一个对象与之关联。由于这种行为被证明比有用更令人困惑,因此添加了relationship.cascade_backrefs
和backref.cascade_backrefs
参数,可以将其设置为False
以禁用它,在 SQLAlchemy 2.0 中完全删除了“cascade backrefs”行为。
对于较旧的 SQLAlchemy 版本,要在当前使用relationship.backref
字符串参数配置的反向引用上设置relationship.cascade_backrefs
为False
,必须首先使用backref()
函数声明反向引用,以便可以传递backref.cascade_backrefs
参数。
或者,可以通过在“未来”模式下使用Session
,通过为Session.future
参数传递True
来全面关闭“cascade backrefs”行为。
另请参阅
cascade_backrefs 行为在 2.0 中已弃用 - SQLAlchemy 2.0 的变更背景。 ### 创建在“传统”模式下的 select() 构造;关键字参数等。
从 SQLAlchemy 1.4 开始,select()
构造已经更新为支持 SQLAlchemy 2.0 中标准的新调用风格。为了在 1.4 系列内保持向后兼容性,该构造在“传统”风格以及“新”风格下都接受参数。
“新”风格的特点是,列和表达式只传递给 select()
构造;对象的任何其他修饰符必须通过后续的方法链传递:
# this is the way to do it going forward stmt = select(table1.c.myid).where(table1.c.myid == table2.c.otherid)
作为对比,在 SQLAlchemy 的传统形式中,像 Select.where()
这样的方法甚至还未添加之前,select()
会是这样的:
# this is how it was documented in original SQLAlchemy versions # many years ago stmt = select([table1.c.myid], whereclause=table1.c.myid == table2.c.otherid)
或者甚至,“whereclause”会被按位置传递:
# this is also how it was documented in original SQLAlchemy versions # many years ago stmt = select([table1.c.myid], table1.c.myid == table2.c.otherid)
多年来,大多数叙述性文档中已经删除了接受的额外“whereclause”和其他参数,导致了一种最为熟悉的调用风格,即将列参数作为列表传递,但没有进一步的参数:
# this is how it's been documented since around version 1.0 or so stmt = select([table1.c.myid]).where(table1.c.myid == table2.c.otherid)
在 select() 不再接受多样化的构造函数参数,列是按位置传递的 文档中以 2.0 迁移 的术语描述了这一变更。
另请参阅
select() 不再接受多样化的构造函数参数,列是按位置传递的
SQLAlchemy 2.0 - 重大迁移指南 ### 通过传递 future=True 到 Session 上,将会忽略通过传统绑定的元数据所定位的绑定。
“绑定元数据”的概念一直存在直到 SQLAlchemy 1.4;截至 SQLAlchemy 2.0,它已被移除。
此错误指的是MetaData.bind
参数,它在 ORM Session
中允许将特定映射类与Engine
相关联的MetaData
对象上。在 SQLAlchemy 2.0 中,Session
必须直接链接到每个Engine
上。也就是说,不能再不带任何参数实例化Session
或sessionmaker
,并将Engine
与MetaData
相关联:
engine = create_engine("sqlite://") Session = sessionmaker() metadata_obj = MetaData(bind=engine) Base = declarative_base(metadata=metadata_obj) class MyClass(Base): ... session = Session() session.add(MyClass()) session.commit()
Engine
必须直接与sessionmaker
或Session
相关联。MetaData
对象不应再与任何引擎相关联:
engine = create_engine("sqlite://") Session = sessionmaker(engine) Base = declarative_base() class MyClass(Base): ... session = Session() session.add(MyClass()) session.commit()
在 SQLAlchemy 1.4 中,当在sessionmaker
或Session
上设置Session.future
标志时,启用此 2.0 样式行为。###此编译对象未绑定到任何引擎或连接
此错误涉及到“绑定元数据”的概念,这是仅存在于 1.x 版本中的传统 SQLAlchemy 模式。当直接从未与任何Engine
相关联的 Core 表达式对象上调用Executable.execute()
方法时会发生此问题:
metadata_obj = MetaData() table = Table("t", metadata_obj, Column("q", Integer)) stmt = select(table) result = stmt.execute() # <--- raises
逻辑预期的是MetaData
对象已经绑定到Engine
:
engine = create_engine("mysql+pymysql://user:pass@host/db") metadata_obj = MetaData(bind=engine)
在上述情况下,从Table
派生的任何语句将隐式使用给定的Engine
来调用该语句。
请注意,绑定元数据的概念在 SQLAlchemy 2.0 中不存在。调用语句的正确方式是通过Connection.execute()
方法的Connection
:
with engine.connect() as conn: result = conn.execute(stmt)
在使用 ORM 时,通过Session
也可以使用类似的功能:
result = session.execute(stmt)
另请参阅
语句执行基础知识### 此连接处于非活动事务状态。请在继续之前完全回滚()
此错误条件已添加到 SQLAlchemy 自版本 1.4 起,不适用于 SQLAlchemy 2.0。该错误指的是将Connection
放入事务中,使用类似Connection.begin()
的方法创建一个进一步的“标记”事务;然后使用Transaction.rollback()
回滚或使用Transaction.close()
关闭“标记”事务,但外部事务仍处于“非活动”状态,必须回滚。
该模式如下:
engine = create_engine(...) connection = engine.connect() transaction1 = connection.begin() # this is a "sub" or "marker" transaction, a logical nesting # structure based on "real" transaction transaction1 transaction2 = connection.begin() transaction2.rollback() # transaction1 is still present and needs explicit rollback, # so this will raise connection.execute(text("select 1"))
上面,transaction2
是一个“标记”事务,表示在外部事务内部的事务逻辑嵌套;虽然内部事务可以通过其 rollback()方法回滚整个事务,但其 commit()方法除了关闭“标记”事务本身的范围外,没有任何效果。调用transaction2.rollback()
的效果是停用transaction1,这意味着它在数据库级别上基本上被回滚,但仍然存在以适应一致的事务嵌套模式。
正确的解决方法是确保外部事务也被回滚:
transaction1.rollback()
此模式在 Core 中并不常用。在 ORM 中,可能会出现类似的问题,这是 ORM 的“逻辑”事务结构的产物;这在 FAQ 条目中有描述“由于刷新期间的先前异常,此会话的事务已回滚。”(或类似)。
在 SQLAlchemy 2.0 中,已删除“子事务”模式,因此这种特定的编程模式不再可用,从而避免了这个错误消息。### 在 SQLAlchemy 2.0 中,<某个函数>将不再<某事>
SQLAlchemy 2.0 对于核心和 ORM 组件中的许多关键 SQLAlchemy 使用模式都表示了一个重大转变。2.0 版本的目标是在 SQLAlchemy 从一开始的基本假设中进行一些轻微调整,并提供一个新的简化的使用模型,希望在核心和 ORM 组件之间更加一致和简约,并且更具有能力。
在 SQLAlchemy 2.0 - 主要迁移指南 中介绍的 SQLAlchemy 2.0 项目包括一个综合的未来兼容性系统,该系统集成到 SQLAlchemy 1.4 系列中,以便应用程序能够清晰、明确地、逐步地升级到完全兼容 2.0 版本。RemovedIn20Warning
弃用警告是这个系统的基础,它提供了对现有代码库中需要修改的行为的指导。关于如何启用此警告的概述在 SQLAlchemy 2.0 弃用模式 中。
另请参阅
SQLAlchemy 2.0 - 主要迁移指南 - 从 1.x 系列的升级过程的概述,以及 SQLAlchemy 2.0 的当前目标和进展。
SQLAlchemy 2.0 弃用模式 - 如何在 SQLAlchemy 1.4 中使用“2.0 弃用模式”的具体指南。
对象正被合并到会话中,沿着反向引用级联
此消息指的是 SQLAlchemy 的“backref cascade”行为,在 2.0 版本中已删除。这是指对象作为已经存在于该会话中的另一个对象的关联而被添加到 Session
中的操作。由于这种行为显示出比有帮助更加令人困惑,添加了 relationship.cascade_backrefs
和 backref.cascade_backrefs
参数,可以将其设置为 False
以禁用它,并且在 SQLAlchemy 2.0 中已完全删除“级联反向引用”行为。
对于较旧的 SQLAlchemy 版本,要在当前使用 relationship.backref
字符串参数配置的反向引用上将 relationship.cascade_backrefs
设置为 False
,必须首先使用 backref()
函数声明反向引用,以便传递 backref.cascade_backrefs
参数。
或者,可以通过在“未来”模式下使用 Session
,将整个“级联反向引用”行为全部关闭,通过为 Session.future
参数传递 True
。
另请参阅
级联反向引用行为在 2.0 中已弃用 - SQLAlchemy 2.0 变更的背景。
以“传统”模式创建的 select() 构造;关键字参数等。
select()
构造已在 SQLAlchemy 1.4 中更新,以支持在 SQLAlchemy 2.0 中标准的新调用风格。为了向后兼容 1.4 系列,该构造接受“传统”风格和“新”风格的参数。
“新”风格的特点是列和表达式仅以位置方式传递给 select()
构造;对象的任何其他修饰符必须使用后续方法链接传递:
# this is the way to do it going forward stmt = select(table1.c.myid).where(table1.c.myid == table2.c.otherid)
作为对比,在 SQLAlchemy 的传统形式中,即使在添加像 Select.where()
这样的方法之前,select()
会像这样:
# this is how it was documented in original SQLAlchemy versions # many years ago stmt = select([table1.c.myid], whereclause=table1.c.myid == table2.c.otherid)
或者甚至“whereclause”将以位置方式传���:
# this is also how it was documented in original SQLAlchemy versions # many years ago stmt = select([table1.c.myid], table1.c.myid == table2.c.otherid)
多年来,大多数叙述性文档中接受的额外“whereclause”和其他参数已被移除,导致调用风格最为熟悉的是作为列参数传递的列表,但没有其他参数:
# this is how it's been documented since around version 1.0 or so stmt = select([table1.c.myid]).where(table1.c.myid == table2.c.otherid)
select() no longer accepts varied constructor arguments, columns are passed positionally 中的文档描述了这一变化,涉及 2.0 迁移。
另请参阅
select() no longer accepts varied constructor arguments, columns are passed positionally
SQLAlchemy 2.0 - 重大迁移指南
通过传统的绑定元数据找到了一个绑定,但由于此会话设置了 future=True,因此会忽略此绑定。
“绑定元数据”的概念一直存在于 SQLAlchemy 1.4 之前;从 SQLAlchemy 2.0 开始已将其删除。
此错误指的是MetaData.bind
参数,该参数位于MetaData
对象上,该对象允许像 ORM Session
这样的对象将特定的映射类与Engine
关联起来。在 SQLAlchemy 2.0 中,Session
必须直接与每个Engine
关联。也就是说,不要实例化Session
或sessionmaker
而不带任何参数,并将Engine
与MetaData
关联:
engine = create_engine("sqlite://") Session = sessionmaker() metadata_obj = MetaData(bind=engine) Base = declarative_base(metadata=metadata_obj) class MyClass(Base): ... session = Session() session.add(MyClass()) session.commit()
相反,Engine
必须直接与sessionmaker
或Session
关联。MetaData
对象不应再与任何引擎相关联:
engine = create_engine("sqlite://") Session = sessionmaker(engine) Base = declarative_base() class MyClass(Base): ... session = Session() session.add(MyClass()) session.commit()
在 SQLAlchemy 1.4 中,当在sessionmaker
或Session
上设置了Session.future
标志时,将启用此 2.0 样式行为。
此 Compiled 对象未绑定到任何 Engine 或 Connection
此错误指的是“绑定元数据”的概念,这是一个仅在 1.x 版本中存在的传统 SQLAlchemy 模式。当直接从未与任何Engine
相关联的 Core 表达式对象上调用Executable.execute()
方法时,就会出现此问题:
metadata_obj = MetaData() table = Table("t", metadata_obj, Column("q", Integer)) stmt = select(table) result = stmt.execute() # <--- raises
逻辑期望的是MetaData
对象已经与Engine
绑定:
engine = create_engine("mysql+pymysql://user:pass@host/db") metadata_obj = MetaData(bind=engine)
在上述情况中,任何从Table
派生的语句,其又派生自MetaData
的语句,将隐式使用给定的Engine
来调用该语句。
请注意,在 SQLAlchemy 2.0 中不存在绑定元数据的概念。调用语句的正确方式是通过Connection.execute()
方法的一个Connection
:
with engine.connect() as conn: result = conn.execute(stmt)
当使用 ORM 时,可以通过Session
提供类似的功能:
result = session.execute(stmt)
请参阅
语句执行基础
此连接处于非活动事务状态。请在继续之前完全 rollback()。
此错误条件已添加到 SQLAlchemy 自 1.4 版本以来,并且不适用于 SQLAlchemy 2.0。该错误是指将Connection
放入事务中,使用类似Connection.begin()
的方法,然后在该范围内创建一个进一步的“标记”事务;然后使用Transaction.rollback()
回滚“标记”事务,或使用Transaction.close()
关闭它,但是外部事务仍然以“非活动”状态存在,必须回滚。
模式如下:
engine = create_engine(...) connection = engine.connect() transaction1 = connection.begin() # this is a "sub" or "marker" transaction, a logical nesting # structure based on "real" transaction transaction1 transaction2 = connection.begin() transaction2.rollback() # transaction1 is still present and needs explicit rollback, # so this will raise connection.execute(text("select 1"))
在上述代码中,transaction2
是一个“标记”事务,它表示外部事务内部的逻辑嵌套;而内部事务可以通过其 rollback()方法回滚整个事务,但是其 commit()方法除了关闭“标记”事务本身的范围外,并不产生任何效果。调用transaction2.rollback()
的效果是停用transaction1,这意味着它在数据库级别上基本上已被回滚,但仍然存在以适应一致的事务嵌套模式。
正确的解决方法是确保外部事务也被回滚:
transaction1.rollback()
此模式在核心中不常用。在 ORM 中,可能会出现类似的问题,这是 ORM 的“逻辑”事务结构的产物;这在常见问题解答条目中有描述:“此会话的事务由于刷新期间的先前异常而已被回滚。”(或类似)。
“子事务”模式在 SQLAlchemy 2.0 中被移除,因此这种特定的编程模式不再可用,从而防止了这个错误消息的出现。
# this is also how it was documented in original SQLAlchemy versions # many years ago stmt = select([table1.c.myid], table1.c.myid == table2.c.otherid)
多年来,大多数叙述性文档中接受的额外“whereclause”和其他参数已被移除,导致调用风格最为熟悉的是作为列参数传递的列表,但没有其他参数:
# this is how it's been documented since around version 1.0 or so stmt = select([table1.c.myid]).where(table1.c.myid == table2.c.otherid)
select() no longer accepts varied constructor arguments, columns are passed positionally 中的文档描述了这一变化,涉及 2.0 迁移。
另请参阅
select() no longer accepts varied constructor arguments, columns are passed positionally
SQLAlchemy 2.0 - 重大迁移指南
通过传统的绑定元数据找到了一个绑定,但由于此会话设置了 future=True,因此会忽略此绑定。
“绑定元数据”的概念一直存在于 SQLAlchemy 1.4 之前;从 SQLAlchemy 2.0 开始已将其删除。
此错误指的是MetaData.bind
参数,该参数位于MetaData
对象上,该对象允许像 ORM Session
这样的对象将特定的映射类与Engine
关联起来。在 SQLAlchemy 2.0 中,Session
必须直接与每个Engine
关联。也就是说,不要实例化Session
或sessionmaker
而不带任何参数,并将Engine
与MetaData
关联:
engine = create_engine("sqlite://") Session = sessionmaker() metadata_obj = MetaData(bind=engine) Base = declarative_base(metadata=metadata_obj) class MyClass(Base): ... session = Session() session.add(MyClass()) session.commit()
相反,Engine
必须直接与sessionmaker
或Session
关联。MetaData
对象不应再与任何引擎相关联:
engine = create_engine("sqlite://") Session = sessionmaker(engine) Base = declarative_base() class MyClass(Base): ... session = Session() session.add(MyClass()) session.commit()
在 SQLAlchemy 1.4 中,当在sessionmaker
或Session
上设置了Session.future
标志时,将启用此 2.0 样式行为。
此 Compiled 对象未绑定到任何 Engine 或 Connection
此错误指的是“绑定元数据”的概念,这是一个仅在 1.x 版本中存在的传统 SQLAlchemy 模式。当直接从未与任何Engine
相关联的 Core 表达式对象上调用Executable.execute()
方法时,就会出现此问题:
metadata_obj = MetaData() table = Table("t", metadata_obj, Column("q", Integer)) stmt = select(table) result = stmt.execute() # <--- raises
逻辑期望的是MetaData
对象已经与Engine
绑定:
engine = create_engine("mysql+pymysql://user:pass@host/db") metadata_obj = MetaData(bind=engine)
在上述情况中,任何从Table
派生的语句,其又派生自MetaData
的语句,将隐式使用给定的Engine
来调用该语句。
请注意,在 SQLAlchemy 2.0 中不存在绑定元数据的概念。调用语句的正确方式是通过Connection.execute()
方法的一个Connection
:
with engine.connect() as conn: result = conn.execute(stmt)
当使用 ORM 时,可以通过Session
提供类似的功能:
result = session.execute(stmt)
请参阅
语句执行基础
此连接处于非活动事务状态。请在继续之前完全 rollback()。
此错误条件已添加到 SQLAlchemy 自 1.4 版本以来,并且不适用于 SQLAlchemy 2.0。该错误是指将Connection
放入事务中,使用类似Connection.begin()
的方法,然后在该范围内创建一个进一步的“标记”事务;然后使用Transaction.rollback()
回滚“标记”事务,或使用Transaction.close()
关闭它,但是外部事务仍然以“非活动”状态存在,必须回滚。
模式如下:
engine = create_engine(...) connection = engine.connect() transaction1 = connection.begin() # this is a "sub" or "marker" transaction, a logical nesting # structure based on "real" transaction transaction1 transaction2 = connection.begin() transaction2.rollback() # transaction1 is still present and needs explicit rollback, # so this will raise connection.execute(text("select 1"))
在上述代码中,transaction2
是一个“标记”事务,它表示外部事务内部的逻辑嵌套;而内部事务可以通过其 rollback()方法回滚整个事务,但是其 commit()方法除了关闭“标记”事务本身的范围外,并不产生任何效果。调用transaction2.rollback()
的效果是停用transaction1,这意味着它在数据库级别上基本上已被回滚,但仍然存在以适应一致的事务嵌套模式。
正确的解决方法是确保外部事务也被回滚:
transaction1.rollback()
此模式在核心中不常用。在 ORM 中,可能会出现类似的问题,这是 ORM 的“逻辑”事务结构的产物;这在常见问题解答条目中有描述:“此会话的事务由于刷新期间的先前异常而已被回滚。”(或类似)。
“子事务”模式在 SQLAlchemy 2.0 中被移除,因此这种特定的编程模式不再可用,从而防止了这个错误消息的出现。