Release: 1.4.25 | Release Date: September 22, 2021

SQLAlchemy 1.4 Documentation

SQLAlchemy 1.4 Documentation

错误信息

本节列出了SQLAlchemy引发或发出的常见错误消息和警告的描述和背景。

sqlAlchemy通常会在特定于sqlAlchemy的异常类的上下文中引发错误。有关这些类的详细信息,请参见 核心异常ORM例外 .

SQLAlchemy错误大致可以分为两类,即 programming-time error 以及 运行时错误 . 由于使用不正确的参数调用函数或方法,或从其他面向配置的方法(如无法解决的映射器配置)调用,会引发编程时间错误。编程时间错误通常是直接的和确定性的。另一方面,运行时错误表示当程序响应某些任意发生的条件(如数据库连接耗尽或发生某些与数据相关的问题)运行时发生的故障。运行时错误更可能出现在正在运行的应用程序的日志中,因为程序在响应加载和遇到的数据时遇到这些状态。

由于运行时错误不像程序运行时那样容易重现,并且经常在响应某些任意条件时发生,因此它们更难调试,也会影响已投入生产的程序。

在本节中,目标是尝试提供一些最常见的运行时错误以及编程时错误的背景。

旧API功能

在“传统”模式下创建的select()构造;关键字参数等。

这个 select() 构造已从SQLAlChemy 1.4更新,以支持将成为中的标准的较新的调用样式 SQLAlchemy 2.0 。为了在过渡期间向后兼容,该构造既接受“遗留”样式的参数,也接受“新”样式的参数。

“新”样式的特征是列和表表达式按位置传递给 select() 仅构造;对象的任何其他修饰符都必须使用后续方法链接传递:

# this is the way to do it going forward
stmt = select(table1.c.myid).where(table1.c.myid == table2.c.otherid)

作为比较,a select() 在SQLAlchemy的遗留形式中,在 Select.where() 甚至添加了,希望:

# 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 Migration .

SQLAlchemy 2.0中的<some function>将不再<something>

SQLAlchemy 2.0预计将是核心组件和ORM组件中各种关键SQLAlchemy使用模式的重大转变。这个版本的目标是对SQLAlchemy早期以来的一些最基本的假设做一点小小的调整,并提供一个新的简化的使用模型,这个模型希望在核心和ORM组件之间更加简洁和一致,并且更加有能力。

介绍时间 迁移到Alchemy ,SQLAlchemy 2.0项目包括一个全面的未来兼容性系统,该系统将集成到SQLAlchemy的1.4系列中,这样应用程序将有一个清晰、明确和增量的升级路径,以便将应用程序迁移到完全兼容2.0。这个 RemovedIn20Warning 弃用警告位于该系统的基础上,用于指导现有代码库中的哪些行为需要修改。有关如何启用此警告的概述,请参阅 SQLAlchemy 2.0弃用模式 .

参见

迁移到Alchemy -概述1.x系列的升级过程,以及sqlalchemy2.0的当前目标和进度。

SQLAlchemy 2.0弃用模式 -关于如何在SQLAlchemy 1.4中使用“2.0弃用模式”的具体指导原则。

已通过旧版绑定元数据找到绑定,但由于在此会话上设置了future=True,因此将忽略此绑定。

SQLAlchemy 2.0中删除了“绑定元数据”的概念。这是指 MetaData.bind 上的参数 MetaData 对象,该对象又允许像ORM这样的对象 Session 将特定映射类与 Engine . 在SQLAlchemy 2.0中 Session 必须链接到每个 Engine 直接。也就是说,不是实例化 Sessionsessionmaker 没有任何参数,并将 EngineMetaData ::

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 必须直接与 sessionmakerSession . 这个 MetaData 对象不应再与任何引擎关联::

engine = create_engine("sqlite://")
Session = sessionmaker(engine)
Base = declarative_base()

class MyClass(Base):
    # ...


session = Session()
session.add(MyClass())
session.commit()

在SQLAlchemy 1.4中,这个 2.0 styleSession.future 标志已设置为 sessionmakerSession .

对象正在沿着backref级联合并到会话中。

此消息指的是SQLAlChemy的“backref casade”行为,详情请参阅 在backrefs上控制级联 。这指的是将对象添加到 Session 因为该会话中已经存在的另一个对象与其相关联。由于这一行为被证明是更令人困惑而不是有帮助的, relationship.cascade_backrefsbackref.cascade_backrefs 添加了参数,可以将其设置为 False 要禁用它,在SQLAlChemy2.0中,“级联后向引用”行为将被完全禁用。

要设置 relationship.cascade_backrefsFalse 在当前使用 relationship.backref 字符串参数,则必须使用 backref() 函数,以便 backref.cascade_backrefs 参数可以传递。

或者,整个“级联后向引用”行为可以通过使用 Session 在“将来”模式中,通过传递 True 对于 Session.future 参数。

参见

在backrefs上控制级联 -级联后向参照行为的完整描述

cascade_backrefs行为在2.0中不推荐删除 -SQLAlChemy 2.0更改的背景。

正在为RAW子句元素自动生成别名

1.4.26 新版功能.

此弃用警告指的是一种非常古老且可能不为人熟知的模式,该模式适用于旧版 Query.join() 方法以及 2.0 style Select.join() 方法,其中联接可以用 relationship() 但目标是 Table 或类映射到的其他核心可选对象,而不是ORM实体,如映射的类或 aliased() 构造::

a1 = Address.__table__

q = s.query(User).\
    join(a1, User.addresses).\
    filter(Address.email_address == 'ed@foo.com').all()

上述模式还允许任意选择,如核心 JoinAlias 对象,但是不会自动调整此元素,这意味着需要直接引用Core元素::

a1 = Address.__table__.alias()

q = s.query(User).\
    join(a1, User.addresses).\
    filter(a1.c.email_address == 'ed@foo.com').all()

指定联接目标的正确方法是始终使用映射的类本身或 aliased 对象,在后一种情况下,使用 PropComparator.of_type() 用于设置别名的修饰符::

# normal join to relationship entity
q = s.query(User).\
    join(User.addresses).\
    filter(Address.email_address == 'ed@foo.com')

# name Address target explicitly, not necessary but legal
q = s.query(User).\
    join(Address, User.addresses).\
    filter(Address.email_address == 'ed@foo.com')

加入别名::

from sqlalchemy.orm import aliased

a1 = aliased(Address)

# of_type() form; recommended
q = s.query(User).\
    join(User.addresses.of_type(a1)).\
    filter(a1.email_address == 'ed@foo.com')

# target, onclause form
q = s.query(User).\
    join(a1, User.addresses).\
    filter(a1.email_address == 'ed@foo.com')

由于表重叠,正在自动生成别名

1.4.26 新版功能.

查询时通常会生成此警告。 Select.join() 方法还是传统的 Query.join() 方法,其中的映射涉及联接表继承。问题是,当在共享公共基表的两个联接继承模型之间联接时,如果不将别名应用于一侧或另一侧,则无法在两个实体之间形成正确的SQL联接;SQLAlChemy将别名应用于联接的右侧。例如,给定一个联接继承映射,如下所示::

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    manager_id = Column(ForeignKey("manager.id"))
    name = Column(String(50))
    type = Column(String(50))

    reports_to = relationship("Manager", foreign_keys=manager_id)

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type,
    }

class Manager(Employee):
    __tablename__ = 'manager'
    id = Column(Integer, ForeignKey('employee.id'), primary_key=True)

    __mapper_args__ = {
        'polymorphic_identity':'manager',
        'inherit_condition': id == Employee.id
    }

上述映射包括 EmployeeManager 上课。因为这两个类都使用“Employee”数据库表,所以从SQL的角度来看,这是一个 self referential relationship 。如果我们想要从两个 EmployeeManager 使用联接建模时,在SQL级别,“Employee”表需要在查询中包括两次,这意味着它必须有别名。当我们使用SQLAlChemy ORM创建这样的联接时,我们会得到如下所示的SQL:

>>> stmt = select(Employee, Manager).join(Employee.reports_to)
>>> print(stmt)
SELECT employee.id, employee.manager_id, employee.name, employee.type, manager_1.id AS id_1, employee_1.id AS id_2, employee_1.manager_id AS manager_id_1, employee_1.name AS name_1, employee_1.type AS type_1 FROM employee JOIN (employee AS employee_1 JOIN manager AS manager_1 ON manager_1.id = employee_1.id) ON manager_1.id = employee.manager_id

在上面,SQL从 employee 表,表示 Employee 查询中的实体。然后,它连接到的右嵌套连接 employee AS employee_1 JOIN manager AS manager_1 ,其中 employee 表被再次声明,但作为匿名别名除外 employee_1 。这是警告消息所指的“自动生成别名”。

当SQLAlChemy加载ORM行时,每个ORM行都包含 Employee 和一个 Manager 对象,ORM必须从上面的 employee_1manager_1 表别名转换为无别名的 Manager 班级。此过程在内部很复杂,并不适合所有API功能,特别是在尝试使用诸如 contains_eager() 具有比此处显示的更深嵌套的查询。由于该模式对于更复杂的场景是不可靠的,并且涉及难以预测和遵循的隐式决策,因此发出警告,并且该模式可能被视为遗留功能。编写此查询的更好方法是使用适用于任何其他自引用关系的相同模式,即使用 aliased() 显式构造。对于联接继承和其他面向联接的映射,通常需要添加 aliased.flat 参数,该参数将允许通过将别名应用于联接中的各个表,而不是将联接嵌入到新子查询中,来为两个或多个表的联接设置别名:

>>> from sqlalchemy.orm import aliased
>>> manager_alias = aliased(Manager, flat=True)
>>> stmt = select(Employee, manager_alias).join(Employee.reports_to.of_type(manager_alias))
>>> print(stmt)
SELECT employee.id, employee.manager_id, employee.name, employee.type, manager_1.id AS id_1, employee_1.id AS id_2, employee_1.manager_id AS manager_id_1, employee_1.name AS name_1, employee_1.type AS type_1 FROM employee JOIN (employee AS employee_1 JOIN manager AS manager_1 ON manager_1.id = employee_1.id) ON manager_1.id = employee.manager_id

如果我们想要使用 contains_eager() 要填充 reports_to 属性,我们引用别名::

>>> stmt =select(Employee).join(
...     Employee.reports_to.of_type(manager_alias)
... ).options(
...     contains_eager(Employee.reports_to.of_type(manager_alias))
... )

而不使用显式 aliased() 对象,在某些更嵌套的情况下, contains_eager() 如果ORM在非常嵌套的上下文中是“自动别名”,则Option没有足够的上下文知道从哪里获取数据。因此,最好不要依赖此特性,而是尽可能地保持SQL结构的显式。

连接和事务

已达到队列池大小限制<x>溢出<y>,连接超时,超时<z>

这可能是遇到的最常见的运行时错误,因为它直接涉及超过配置限制的应用程序的工作负载,该限制通常适用于几乎所有的SQLAlchemy应用程序。

以下几点总结了这个错误的含义,从大多数SQLAlchemy用户应该已经熟悉的最基本的点开始。

  • 默认情况下,SQLAlchemy引擎对象使用连接池 -这意味着当使用SQL数据库连接资源时, Engine 对象,然后 releases 这个资源,数据库连接本身仍然连接到数据库,并返回到一个内部队列,在那里可以再次使用它。即使代码似乎正在结束与数据库的对话,在许多情况下,应用程序仍将保持固定数量的数据库连接,这些连接将一直保持到应用程序结束或池被显式释放为止。

  • 由于池的原因,当应用程序使用SQL数据库连接时,通常从使用 Engine.connect() 或使用ORM进行查询时 Session ,此活动不一定在获取连接对象时建立到数据库的新连接;而是为连接查询连接池,该连接通常从要重新使用的池中检索现有连接。如果没有可用的连接,池将创建一个新的数据库连接,但前提是池没有超过配置的容量。

  • 在大多数情况下使用的默认池被调用 QueuePool . 当您要求此池为您提供一个连接但没有可用的连接时,它将创建一个新连接。 如果播放中的连接总数小于配置的值 . 该值等于 池大小加上最大溢出 . 这意味着如果您将引擎配置为:

    engine = create_engine("mysql://u:p@host/db", pool_size=10, max_overflow=20)

    以上 Engine 将允许 最多30个连接 可随时使用,不包括与发动机分离或失效的连接。如果一个新连接的请求到达,并且应用程序的其他部分已经使用了30个连接,那么在超时并引发此错误消息之前,连接池将阻塞一段固定的时间。

    为了允许同时使用更多的连接,可以使用 create_engine.pool_sizecreate_engine.max_overflow 传递到的参数 create_engine() 功能。等待连接可用的超时使用 create_engine.pool_timeout 参数。

  • 可以通过设置将池配置为具有无限制的溢出 create_engine.max_overflow 到值“-1”。使用此设置,池仍将保持固定的连接池,但是它不会在请求新连接时阻塞;相反,如果没有可用的连接,它将无条件创建新连接。

    但是,以这种方式运行时,如果应用程序出现问题,即它正在耗尽所有可用的连接资源,则它最终将达到数据库本身配置的可用连接限制,这将再次返回错误。更严重的是,当应用程序耗尽连接数据库时,它通常会导致大量资源在失败之前耗尽,并且还会干扰其他依赖于能够连接到数据库的应用程序和数据库状态机制。

    考虑到上述情况,可以将连接池视为 连接用安全阀 对恶意应用程序提供关键的保护层,导致整个数据库对所有其他应用程序不可用。当收到此错误消息时,最好使用过多的连接来修复问题和/或适当地配置限制,而不是允许不受限制的溢出,这实际上并不能解决基础问题。

是什么导致应用程序用尽所有可用的连接?

  • 应用程序根据池的配置值部署了太多的并发请求,无法工作。 -这是最直接的原因。如果您有一个应用程序在一个线程池中运行,该线程池允许30个并发线程,每个线程使用一个连接,如果您的池没有配置为允许至少30个连接一次签出,那么一旦应用程序收到足够的并发请求,就会出现此错误。解决方案是提高池的限制或减少并发线程的数量。

  • 应用程序没有返回到池的连接 -这是第二个最常见的原因,即应用程序正在使用连接池,但程序未能 release 这些连接,而不是让它们打开。连接池和ORM Session 一定要有这样的逻辑:当会话和/或连接对象被垃圾收集时,它会导致底层连接资源被释放,但是这种行为不能及时地释放资源。

    出现这种情况的一个常见原因是应用程序使用ORM会话而不调用 Session.close() 在他们身上,涉及到那个会议的工作已经完成。解决方案是确保使用ORM或引擎绑定的ORM会话 Connection 如果使用core,则对象将在正在执行的工作结束时通过适当的 .close() 方法,或者使用一个可用的上下文管理器(例如“with:”语句)来正确释放资源。

  • The application is attempting to run long-running transactions -数据库事务是非常昂贵的资源,应该 永远不要在等待某个事件发生时处于空闲状态 . 如果应用程序正在等待用户按下按钮,或者结果从长时间运行的作业队列中出来,或者保持对浏览器的持久连接, 不要一直打开数据库事务 . 由于应用程序需要与数据库一起工作并与事件交互,因此在该点打开一个短期事务,然后关闭它。

  • 应用程序死锁 -此外,此错误的一个常见原因是更难以理解,如果应用程序由于应用程序端死锁或数据库端死锁而无法完成连接的使用,则应用程序可以使用所有可用的连接,从而导致接收此错误的其他请求。僵局的原因包括:

    • 使用隐式异步系统,如gevent或eventlet,而没有正确地对所有套接字库和驱动程序进行MonkeyPatching,或者在没有完全覆盖所有MonkeyPatching驱动程序方法方面存在缺陷,或者在异步系统用于CPU绑定的工作负载时不太常见,而使用数据库资源的greenlet只是在等待。太长时间不能照顾他们。隐式或显式异步编程框架对于绝大多数关系数据库操作都不是必需的或合适的;如果应用程序必须在某些功能领域使用异步系统,那么最好是在传统线程中运行面向数据库的业务方法,这些线程将消息传递给异步部分o。应用程序。

    • 数据库端死锁,例如行相互死锁

    • 线程错误,例如互斥锁中的互斥锁,或调用同一线程中已锁定的互斥锁

请记住,使用池的另一种选择是完全关闭池。见剖面图 切换池实现 作为背景。但是,请注意,当出现此错误消息时, 总是 由于应用程序本身存在更大的问题,池只是有助于更快地发现问题。

在回滚无效事务之前无法重新连接

此错误条件指的是 Connection 由于数据库断开连接检测或显式调用 Connection.invalidate() ,但仍存在由启动的事务 Connection.begin() 方法。当连接失效时,任何 Transaction 正在进行的进程现在处于无效状态,必须显式回滚才能将其从 Connection .

此连接处于非活动事务上。请在继续之前完全回滚()。

从1.4版起,此错误条件已添加到sqlAlchemy中。错误指的是 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 是一个“marker”事务,它表示事务在外部事务中的逻辑嵌套;虽然内部事务可以通过其rollback()方法回滚整个事务,但它的commit()方法除了关闭“marker”事务本身的作用域外没有任何效果。号召 transaction2.rollback() 具有的效果 去激活 transaction1意味着它基本上是在数据库级别回滚的,但是为了适应一致的事务嵌套模式,它仍然存在。

正确的解决方案是确保外部事务也被回滚:

transaction1.rollback()

这种模式不常用于核心。在ORM中,类似的问题也可能发生,这是ORM的“逻辑”事务结构的产物;在 “由于在刷新过程中出现以前的异常,此会话的事务已回滚。”(或类似) .

在sqlalchemy2.0中将删除“subtransaction”模式,这样这个特定的编程模式将不再可用,并且这个错误消息也不再出现在Core中。

数据库应用程序接口错误

python数据库API(或dbapi)是一种数据库驱动程序规范,位于 Pep-249 . 此API指定了一组异常类,这些类可以适应数据库的所有故障模式。

SQLAlchemy不会直接生成这些异常。相反,它们是从数据库驱动程序截取的,并由SQLAlchemy提供的异常进行包装。 DBAPIError 但是,异常中的消息是 由驱动程序生成,而不是由sqlAlchemy生成 .

InterfaceError

对与数据库接口而不是数据库本身相关的错误引发异常。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

这个 InterfaceError 有时由驱动程序在数据库连接被删除或无法连接到数据库的上下文中引发。有关如何处理此问题的提示,请参阅部分 处理断开连接 .

DatabaseError

对与数据库本身相关的错误(而不是正在传递的接口或数据)引发异常。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

DataError

由于处理的数据出现问题(如被零除、数值超出范围等)导致的错误引发异常。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

OperationalError

对与数据库操作相关且不一定在程序员控制下的错误引发的异常,例如发生意外断开、找不到数据源名称、无法处理事务、处理过程中发生内存分配错误等。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

这个 OperationalError 是驱动程序在要删除或无法连接到数据库的数据库连接上下文中使用的最常见(但不是唯一)错误类。有关如何处理此问题的提示,请参阅部分 处理断开连接 .

IntegrityError

当数据库的关系完整性受到影响时引发异常,例如外键检查失败。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

InternalError

当数据库遇到内部错误时引发异常,例如光标不再有效、事务不同步等。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

这个 InternalError 有时由驱动程序在数据库连接被删除或无法连接到数据库的上下文中引发。有关如何处理此问题的提示,请参阅部分 处理断开连接 .

ProgrammingError

因编程错误引发的异常,例如,表未找到或已存在、SQL语句中的语法错误、指定的参数数目错误等。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

这个 ProgrammingError 有时由驱动程序在数据库连接被删除或无法连接到数据库的上下文中引发。有关如何处理此问题的提示,请参阅部分 处理断开连接 .

NotSupportedError

在使用数据库不支持的方法或数据库API时引发异常,例如,在不支持事务或已关闭事务的连接上请求.rollback()。

这个错误是 DBAPI Error 并且源于数据库驱动程序(DBAPI),而不是SQLAlchemy本身。

SQL表达式语言

编译器strsqlcompiler无法呈现类型为<element type>

当试图将包含不属于默认编译的元素的SQL表达式构造字符串化时,通常会发生此错误;在这种情况下,该错误将针对 StrSQLCompiler 班级。在不太常见的情况下,如果将错误类型的SQL表达式用于特定类型的数据库后端,也会发生这种情况;在这种情况下,将命名其他类型的SQL编译器类,例如 SQLCompilersqlalchemy.dialects.postgresql.PGCompiler . 下面的指南更具体地描述了“细化”用例,但也描述了一般的背景。

通常,核心SQL构造或ORM Query 对象可以直接字符串化,例如当我们使用 print() ::

>>> from sqlalchemy import column
>>> print(column('x') == 5)
x = :x_1

当上面的SQL表达式被字符串化时, StrSQLCompiler 使用编译器类,这是一种特殊的语句编译器,当构造在没有任何方言特定信息的情况下进行字符串化时调用。

但是,有许多特定于某种特定类型的数据库方言的构造,其中 StrSQLCompiler 不知道如何变成字符串,比如PostgreSQL "insert on conflict" 结构:

>>> from sqlalchemy.dialects.postgresql import insert
>>> from sqlalchemy import table, column
>>> my_table = table('my_table', column('x'), column('y'))
>>> insert_stmt = insert(my_table).values(x='foo')
>>> insert_stmt = insert_stmt.on_conflict_do_nothing(
...     index_elements=['y']
... )
>>> print(insert_stmt)
Traceback (most recent call last):

...

sqlalchemy.exc.UnsupportedCompilationError:
Compiler <sqlalchemy.sql.compiler.StrSQLCompiler object at 0x7f04fc17e320>
can't render element of type
<class 'sqlalchemy.dialects.postgresql.dml.OnConflictDoNothing'>

为了将特定于特定后端的构造进行字符串化,需要 ClauseElement.compile() 必须使用方法,传递 Engine 或A Dialect 将调用正确编译器的对象。下面我们使用PostgreSQL方言:

>>> from sqlalchemy.dialects import postgresql
>>> print(insert_stmt.compile(dialect=postgresql.dialect()))
INSERT INTO my_table (x) VALUES (%(x)s) ON CONFLICT (y) DO NOTHING

对于ORM Query 对象,可以使用 Query.statement 访问器:

statement = query.statement
print(statement.compile(dialect=postgresql.dialect()))

有关SQL元素的直接字符串化/编译的更多详细信息,请参阅下面的FAQ链接。

类型错误:<operator>在“columnproperty”实例和<something>实例之间不受支持

当尝试使用 column_property()deferred() 对象在SQL表达式的上下文中,通常位于声明性的,例如:

class Bar(Base):
    __tablename__ = 'bar'

    id = Column(Integer, primary_key=True)
    cprop = deferred(Column(Integer))

    __table_args__ = (
        CheckConstraint(cprop > 5),
    )

上面, cprop 属性在映射之前被内联使用,但是 cprop 属性不是 Column 这是一个 ColumnProperty 它是一个临时对象,因此不具有 Column 对象或 InstrumentedAttribute 将映射到 Bar 一旦声明过程完成,则初始化。

ColumnProperty 确实有 __clause_element__() 方法,它允许它在一些面向列的上下文中工作,它不能在上面所示的开放式比较上下文中工作,因为它没有python __eq__() 方法,它可以将与数字“5”的比较解释为SQL表达式,而不是常规的Python比较。

解决方案是访问 Column 直接使用 ColumnProperty.expression 属性:

class Bar(Base):
    __tablename__ = 'bar'

    id = Column(Integer, primary_key=True)
    cprop = deferred(Column(Integer))

    __table_args__ = (
        CheckConstraint(cprop.expression > 5),
    )

此编译对象未绑定到任何引擎或连接

此错误是指“绑定元数据”的概念,如 无连接执行,隐式执行 . 当调用 Executable.execute() 直接从与任何 Engine ::

metadata_obj = MetaData()
table = Table('t', metadata_obj, Column('q', Integer))

stmt = select(table)
result = stmt.execute()   # <--- raises

逻辑预期的是 MetaData 对象已经 跳跃 到A Engine ::

engine = create_engine("mysql+pymysql://user:pass@host/db")
metadata_obj = MetaData(bind=engine)

在上文中,任何源自 Table 这反过来又是从 MetaData 将隐式地使用给定的 Engine 以便调用语句。

注意,绑定元数据的概念是 遗产模式 在大多数情况下 非常沮丧 . 调用语句的最佳方法是将其传递给 Connection.execute() A方法 Connection ::

with engine.connect() as conn:
  result = conn.execute(stmt)

使用ORM时,可通过 Session ::

result = session.execute(stmt)

绑定参数需要一个值(在参数组中)

当语句使用 bindparam() 隐式或显式,并且在执行语句时不提供值::

stmt = select(table.c.column).where(table.c.id == bindparam('my_param'))

result = conn.execute(stmt)

上面没有为参数“my_param”提供任何值。正确的方法是提供一个值:

result = conn.execute(stmt, my_param=12)

当消息采用“参数组中的绑定参数<x>需要一个值”的形式时,消息引用的是“ExecuteMany”执行样式。在这种情况下,该语句通常是一个插入、更新或删除语句,并且正在传递参数列表。在这种格式中,语句可以动态生成,以包括参数列表中给定的每个参数的参数位置,在这里它将使用 第一组参数 确定这些应该是什么。

例如,下面的语句是根据需要参数“a”、“b”和“c”的第一个参数集计算的-这些名称决定了语句的最终字符串格式,该格式将用于列表中的每一组参数。由于第二个条目不包含“b”,因此会生成以下错误:

m = MetaData()
t = Table(
    't', m,
    Column('a', Integer),
    Column('b', Integer),
    Column('c', Integer)
)

e.execute(
    t.insert(), [
        {"a": 1, "b": 2, "c": 3},
        {"a": 2, "c": 4},
        {"a": 3, "b": 4, "c": 5},
    ]
)

sqlalchemy.exc.StatementError: (sqlalchemy.exc.InvalidRequestError)
A value is required for bind parameter 'b', in parameter group 1
[SQL: u'INSERT INTO t (a, b, c) VALUES (?, ?, ?)']
[parameters: [{'a': 1, 'c': 3, 'b': 2}, {'a': 2, 'c': 4}, {'a': 3, 'c': 5, 'b': 4}]]

由于需要“B”,请将其作为 None 以便插入:

e.execute(
    t.insert(), [
        {"a": 1, "b": 2, "c": 3},
        {"a": 2, "b": None, "c": 4},
        {"a": 3, "b": 4, "c": 5},
    ]
)

应为FROM子句,但得到了SELECT。要创建FROM子句,请使用.subquery()方法

这是指自SQLAlchemy 1.4起所做的更改,其中由函数(如 select() ,但也包括诸如联合和文本选择表达式之类的内容不再被认为是 FromClause 对象,如果不将它们包装在 Subquery 第一。这是核心概念上的一个重大变化,其全部理论基础在 select语句不再被隐式地视为FROM子句 .

举例如下:

m = MetaData()
t = Table(
   't', m,
   Column('a', Integer),
   Column('b', Integer),
   Column('c', Integer)
)
stmt = select(t)

上面, stmt 表示select语句。当我们想使用 stmt 直接作为另一个select中的from子句,例如,如果我们试图从中进行选择:

new_stmt_1 = select(stmt)

或者如果我们想在FROM子句中使用它,例如在联接中:

new_stmt_2 = select(some_table).select_from(some_table.join(stmt))

在以前的SQLAlchemy版本中,在另一个select中使用select将生成一个带括号的未命名子查询。在大多数情况下,这种形式的SQL并不是很有用,因为像MySQL和PostgreSQL这样的数据库要求FROM子句中的子查询具有命名别名,这意味着使用 SelectBase.alias() 方法或自1.4起使用 SelectBase.subquery() 方法生成此。在其他数据库中,子查询有一个名称来解决将来在子查询中对列名称的引用上的任何歧义,这一点仍然更加清楚。

除了上述实际原因之外,还有许多其他面向SQL炼金术的原因正在进行更改。因此,上述两个陈述的正确形式要求 SelectBase.subquery() 用途:

subq = stmt.subquery()

new_stmt_1 = select(subq)

new_stmt_2 = select(some_table).select_from(some_table.join(subq))

对象关系映射

父实例<x>未绑定到会话;(延迟加载/延迟加载/刷新等)操作无法继续

这很可能是处理ORM时最常见的错误消息,并且它是由于ORM广泛使用的一种技术的性质而发生的,即 lazy loading . 延迟加载是一种常见的对象关系模式,通过这种模式,ORM持久化的对象保持对数据库本身的代理,这样当访问对象上的各种属性时,可以从数据库中检索它们的值。 懒洋洋地 . 这种方法的优点是,可以从数据库中检索对象,而不必一次加载所有属性或相关数据,而只需在那时传递请求的数据。主要缺点基本上是优势的镜像,也就是说,如果加载了许多已知在所有情况下都需要特定数据集的对象,则逐段加载这些额外数据是浪费的。

除了通常的效率问题之外,延迟加载的另一个警告是,为了继续进行延迟加载,对象必须 与会话保持关联 以便能够检索其状态。此错误消息表示某个对象已与它的 Session 并被要求从数据库中延迟加载数据。

最常见的原因是对象从 Session 会话本身已关闭,通常通过 Session.close() 方法。然后,这些对象将继续存在以供进一步访问,通常在Web应用程序中,这些对象被传递到服务器端模板引擎,并被要求提供它们无法加载的其他属性。

通过两种通用技术来缓解此错误:

  • 不要过早关闭会话 -通常,应用程序会在将相关对象传递给其他系统之前关闭一个事务,然后由于这个错误而失败。有时事务不需要这么快关闭;例如,Web应用程序在呈现视图之前关闭事务。这通常是以“正确性”的名义进行的,但可能被视为“封装”的错误应用,因为这个术语指的是代码组织,而不是实际操作。使用ORM对象的模板正在使用 proxy pattern 这样就可以从调用者那里封装数据库逻辑。如果 Session 可以一直打开直到对象的寿命结束,这是最好的方法。

  • 把所有需要的东西提前装上 -通常不可能保持事务的开放性,尤其是在更复杂的应用程序中,这些应用程序需要将对象传递给其他系统,而这些系统即使在同一进程中也无法在同一上下文中运行。在这种情况下,应用程序应该尝试适当地使用 eager loading 以确保对象预先得到它们需要的东西。

    使用这种方法时,通常需要 Session.expire_on_commit 参数设置为 False ,所以在 Session.commit() 操作,则会话中的对象不是 expired ,如果随后访问它们的属性,则会导致延迟加载。此外 Session.rollback() 方法无条件地终止 Session 在无错误的情况下也应避免。

    参见

    关系加载技术 -有关预加载和其他面向关系加载技术的详细文档

    提交 -会话提交的背景

    刷新/过期 -属性过期背景

由于在刷新过程中出现以前的异常,此会话的事务已回滚。

冲洗过程 Session ,描述于 冲洗 如果遇到错误,将回滚数据库事务,以保持内部一致性。但是,一旦发生这种情况,会话的事务现在处于“非活动”状态,并且必须由调用应用程序显式回滚,这与如果没有发生故障则需要显式提交的方式相同。

在使用ORM时,这是一个常见的错误,通常适用于其周围还没有正确“框架”的应用程序。 Session 操作。有关更多详细信息,请参见 “由于在刷新过程中出现以前的异常,此会话的事务已回滚。”(或类似) .

对于relationship<relationship>,delete orphan cascade通常只在一对多关系的“一”侧配置,而不是在多对一或多对多关系的“多”侧配置。

当“删除孤立项”时出现此错误 cascade 设置为多对一或多对多关系,例如:

class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)

    bs = relationship("B", back_populates="a")


class B(Base):
    __tablename__ = "b"
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey("a.id"))

    # this will emit the error message when the mapper
    # configuration step occurs
    a = relationship("A", back_populates="bs", cascade="all, delete-orphan")

configure_mappers()

上面的“删除孤立项”设置 B.a 表示 B 引用特定 A 被删除,即 A 然后也应该删除。也就是说,它表示被删除的“孤儿”是 A 对象,当 B 指的是它被删除了。

“删除孤立”级联模型不支持此功能。“孤立”的考虑仅限于删除单个对象,然后该对象将引用零个或多个对象,这些对象现在已通过该单个删除而“孤立”,这将导致这些对象也被删除。换言之,它只被设计为基于删除每个孤立对象一个且仅一个“父”对象来跟踪“孤儿”的创建,这是一对多关系中的自然情况,即删除“一”侧的对象会导致随后删除“多”侧的相关项。

以上支持此功能的映射将把级联设置放在一对多的一侧,如下所示:

class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)

    bs = relationship("B", back_populates="a", cascade="all, delete-orphan")


class B(Base):
    __tablename__ = "b"
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey("a.id"))

    a = relationship("A", back_populates="bs")

当一个 A 被删除,所有 B 它引用的对象也将被删除。

然后,错误消息继续建议 relationship.single_parent 旗子。此标志可用于强制一个关系,即能够有多个对象引用一个特定对象,实际上只有 one 对象一次引用它。它用于遗留数据库模式或其他不太理想的数据库模式,其中外键关系表示“多”集合,但实际上只有一个对象实际引用给定的目标对象。这种不常见的情况可以用上面的例子来说明:

class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)

    bs = relationship("B", back_populates="a")


class B(Base):
    __tablename__ = "b"
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey("a.id"))

    a = relationship(
        "A",
        back_populates="bs",
        single_parent=True,
        cascade="all, delete-orphan",
    )

上面的配置将安装一个验证器,该验证器将只强制执行一个验证器 B 可能与 A 当时,在 B.a 关系:

>>> b1 = B()
>>> b2 = B()
>>> a1 = A()
>>> b1.a = a1
>>> b2.a = a1
sqlalchemy.exc.InvalidRequestError: Instance <A at 0x7eff44359350> is
already associated with an instance of <class '__main__.B'> via its
B.a attribute, and is only allowed a single parent.

注意,这个验证器的作用域是有限的,不会阻止通过另一个方向创建多个“父对象”。例如,它不会检测到相同的设置 A.bs

>>> a1.bs = [b1, b2]
>>> session.add_all([a1, b1, b2])
>>> session.commit()
INSERT INTO a DEFAULT VALUES () INSERT INTO b (a_id) VALUES (?) (1,) INSERT INTO b (a_id) VALUES (?) (1,)

但是,以后事情不会如预期的那样进行,因为“删除孤儿”级联将继续在 单一的 引导对象,意味着如果我们删除 任何一个B 对象 A 已删除。其他的 B 停留在那里,ORM通常足够聪明,可以将外键属性设置为NULL,但这通常不是我们想要的:

>>> session.delete(b1)
>>> session.commit()
UPDATE b SET a_id=? WHERE b.id = ? (None, 2) DELETE FROM b WHERE b.id = ? (1,) DELETE FROM a WHERE a.id = ? (1,) COMMIT

对于以上所有的例子,类似的逻辑适用于多对多关系的演算;如果一个多对多关系在一个方面设置single廑parent=True,则该方可以使用“delete orphan”级联,但是这不太可能是某人真正想要的,因为多对多关系的意义在于可以有很多指向任一方向的对象。

总的来说,“删除孤立”级联通常应用于一对多关系的“一”侧,因此它删除“多”侧的对象,而不是相反。

在 1.3.18 版更改: 在多对一或多对多关系中使用“删除孤立项”错误消息的文本已更新为更具描述性。

Instance<Instance>已经通过其<attribute>属性与<Instance>的实例关联,并且只允许一个父实例。

relationship.single_parent 使用标志,并且一次将多个对象指定为对象的“父对象”。

给定以下映射:

class A(Base):
    __tablename__ = "a"

    id = Column(Integer, primary_key=True)


class B(Base):
    __tablename__ = "b"
    id = Column(Integer, primary_key=True)
    a_id = Column(ForeignKey("a.id"))

    a = relationship(
        "A",
        single_parent=True,
        cascade="all, delete-orphan",
    )

意图表明 B 对象可以引用特定的 A 一次对象:

>>> b1 = B()
>>> b2 = B()
>>> a1 = A()
>>> b1.a = a1
>>> b2.a = a1
sqlalchemy.exc.InvalidRequestError: Instance <A at 0x7eff44359350> is
already associated with an instance of <class '__main__.B'> via its
B.a attribute, and is only allowed a single parent.

当此错误意外发生时,通常是因为 relationship.single_parent 已应用标志以响应中描述的错误消息 对于relationship<relationship>,delete orphan cascade通常只在一对多关系的“一”侧配置,而不是在多对一或多对多关系的“多”侧配置。 ,而问题实际上是对“删除孤立项”级联设置的误解。查看该消息以了解详细信息。

关系X会将列Q复制到列P,这与关系冲突:‘Y’

此警告是指两个或多个关系在刷新时将数据写入同一列,但ORM没有任何方法将这些关系协调在一起。根据具体情况,解决方案可能是需要使用 relationship.back_populates ,或者一个或多个关系应该配置为 relationship.viewonly 以防止写入冲突,或者有时配置完全是故意的,应该配置 relationship.overlaps 让每个警告都安静下来。

对于缺失的典型示例 relationship.back_populates ,给定以下映射:

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
    children = relationship("Child")


class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key=True)
    parent_id = Column(ForeignKey("parent.id"))
    parent = relationship("Parent")

上述映射将生成警告:

SAWarning: relationship 'Child.parent' will copy column parent.id to column child.parent_id,
which conflicts with relationship(s): 'Parent.children' (copies parent.id to child.parent_id).

人际关系 Child.parentParent.children 似乎有冲突。解决办法是将 relationship.back_populates ::

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
    children = relationship("Child", back_populates="parent")


class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key=True)
    parent_id = Column(ForeignKey("parent.id"))
    parent = relationship("Parent", back_populates="children")

对于“重叠”情况可能是故意的且无法解决的更定制的关系, relationship.overlaps 参数可以指定警告不应对其生效的关系的名称。这通常发生在与同一基础表的两个或多个关系中,这些关系包括自定义 relationship.primaryjoin 在每种情况下限制相关项目的条件:

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
    c1 = relationship(
        "Child",
        primaryjoin="and_(Parent.id == Child.parent_id, Child.flag == 0)",
        backref="parent",
        overlaps="c2, parent"
    )
    c2 = relationship(
        "Child",
        primaryjoin="and_(Parent.id == Child.parent_id, Child.flag == 1)",
        overlaps="c1, parent"
    )


class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key=True)
    parent_id = Column(ForeignKey("parent.id"))

    flag = Column(Integer)

上面,ORM将知道 Parent.c1Parent.c2Child.parent 是故意的。

对象无法转换为“”Persistent“”状态,因为此标识映射不再有效。“”

1.4.26 新版功能.

添加此消息是为了适应以下情况 Result 将生成ORM对象的对象在原始 Session 已关闭,或以其他方式具有其 Session.expunge_all() 调用的方法。当一个 Session 一次删除所有对象,内部 identity map 由那个使用 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()

上述情况通常会 not 在使用 asyncio ORM扩展,如当 AsyncSession 返回同步样式 Result ,则在执行该语句时已经预先缓冲了结果。这是为了允许辅助急切加载程序调用,而不需要额外的 await 打电话。

来预先缓冲上述情况下的结果,请使用常规 Session 以同样的方式 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 已经清除了它们,它们将在 detached 州政府。

注解

上面提到的“预缓存”与“未缓存” Result 对象引用ORM将传入的原始数据库行从 DBAPI 转换为ORM对象。这并不意味着林中是否 cursor 表示来自DBAPI的挂起结果的对象本身本身是缓冲或非缓冲的,因为这本质上是较低的缓冲层。有关缓冲的背景信息,请参阅 cursor 结果本身,请参阅小节 使用服务器端游标(即流结果)

AsyncIO异常

AwaitRequired

SQLAlChemy异步模式要求使用异步驱动程序连接到数据库。尝试将异步版本的SQLAlChemy与不兼容的 DBAPI

MissingGreenlet

对异步的呼叫 DBAPI 在通常由SQLAlChemy AsyncIO代理类设置的greenlet派生上下文之外启动。通常,在未使用提供的异步API的情况下,在意外位置尝试IO时,会出现此错误。在使用ORM时,这可能是由于延迟加载尝试造成的,在将SQLAlChemy与AsyncIO方言一起使用时不支持这种情况。

参见

使用AsyncSession时防止隐式IO -涵盖可能发生此问题的大多数ORM场景以及如何缓解。

没有可用的检查

使用 inspect() 函数直接在 AsyncConnectionAsyncEngine 对象当前不受支持,因为尚不存在可等待的 Inspector 对象可用。而是通过使用 inspect() 函数,以使其引用基础 AsyncConnection.sync_connection 属性的属性。 AsyncConnection 对象; 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()
        )

参见

使用检查器检查架构对象 -使用的其他示例 inspect() 扩展名为Asyncio。

核心异常类

核心异常 对于核心异常类。

ORM异常类

ORM例外 用于ORM异常类。

Previous: 第三方集成问题 Next: 更改和迁移