上下文/线程局部会话

回想一下我应该何时构造会话、何时提交会话以及何时关闭会话?部分,其中介绍了“会话作用域”的概念,重点是 Web 应用程序以及将 Session 的作用域与 Web 请求的作用域链接的做法。大多数现代 Web 框架都包含集成工具,以便可以自动管理 Session 的作用域,并且应尽可能使用这些工具。

SQLAlchemy 包含其自身的辅助对象,该对象有助于建立用户定义的 Session 作用域。第三方集成系统也使用它来帮助构建其集成方案。

该对象是 scoped_session 对象,它表示 Session 对象的注册表。如果您不熟悉注册表模式,可以在 企业应用架构模式 中找到一个很好的介绍。

警告

scoped_session 注册表默认使用 Python threading.local() 来跟踪 Session 实例。这不一定与所有应用程序服务器兼容,特别是那些使用 greenlet 或其他形式的并发控制的服务器,当在中高并发场景中使用时,可能会导致竞争条件(例如,随机发生的故障)。请阅读下面的 线程局部作用域在 Web 应用程序中使用线程局部作用域,以更全面地了解使用 threading.local() 跟踪 Session 对象的含义,并在使用不基于传统线程的应用程序服务器时考虑更明确的作用域划分方式。

注意

scoped_session 对象是一个非常流行且有用的对象,被许多 SQLAlchemy 应用程序使用。但是,重要的是要注意,它仅代表解决 Session 管理问题的一种方法。如果您是 SQLAlchemy 的新手,尤其是当“线程局部变量”这个术语对您来说很陌生时,我们建议您尽可能先熟悉现成的集成系统,例如 Flask-SQLAlchemyzope.sqlalchemy

scoped_session 通过调用它来构造,并传递一个工厂,该工厂可以创建新的 Session 对象。工厂只是在调用时生成新对象的某种东西,对于 Session,最常见的工厂是 sessionmaker,在本节前面已介绍过。下面我们说明这种用法

>>> from sqlalchemy.orm import scoped_session
>>> from sqlalchemy.orm import sessionmaker

>>> session_factory = sessionmaker(bind=some_engine)
>>> Session = scoped_session(session_factory)

我们创建的 scoped_session 对象现在将在我们“调用”注册表时调用 sessionmaker

>>> some_session = Session()

上面,some_sessionSession 的一个实例,我们现在可以使用它与数据库通信。同一个 Session 也存在于我们创建的 scoped_session 注册表中。如果我们第二次调用注册表,我们会得到相同Session

>>> some_other_session = Session()
>>> some_session is some_other_session
True

这种模式允许应用程序的不同部分调用全局 scoped_session,以便所有这些区域可以共享同一个会话,而无需显式传递它。我们注册表中建立的 Session 将一直存在,直到我们显式告诉注册表处置它,通过调用 scoped_session.remove()

>>> Session.remove()

scoped_session.remove() 方法首先调用当前 Session 上的 Session.close(),这具有释放 Session 拥有的任何连接/事务资源的效果,然后丢弃 Session 本身。“释放”在这里意味着连接被返回到其连接池,并且任何事务状态都被回滚,最终使用底层 DBAPI 连接的 rollback() 方法。

此时,scoped_session 对象是“空的”,并且在再次调用时将创建一个新的 Session。如下所示,这与我们之前的 Session 不同

>>> new_session = Session()
>>> new_session is some_session
False

上述一系列步骤说明了“注册表”模式的要点。掌握了这个基本概念后,我们可以讨论这种模式如何进行的一些细节。

隐式方法访问

scoped_session 的工作很简单;为所有需要它的人保留一个 Session。作为更透明地访问此 Session 的一种方法,scoped_session 还包括代理行为,这意味着注册表本身可以像 Session 一样直接处理;当在此对象上调用方法时,它们会代理到注册表维护的底层 Session

Session = scoped_session(some_factory)

# equivalent to:
#
# session = Session()
# print(session.scalars(select(MyClass)).all())
#
print(Session.scalars(select(MyClass)).all())

上面的代码完成的任务与获取当前 Session 的任务相同,通过调用注册表,然后使用该 Session

线程局部作用域

熟悉多线程编程的用户会注意到,将任何东西表示为全局变量通常不是一个好主意,因为它意味着全局对象将由多个线程并发访问。Session 对象完全设计为以非并发方式使用,就多线程而言,这意味着“一次只能在一个线程中使用”。因此,我们上面 scoped_session 用法的示例,其中同一个 Session 对象在多次调用中保持不变,表明需要某种过程来确保跨多个线程的多次调用实际上不会获得对同一个会话的句柄。我们将此概念称为线程局部存储,这意味着,使用一个特殊的对象,它将为每个应用程序线程维护一个不同的对象。Python 通过 threading.local() 构造提供此功能。scoped_session 对象默认使用此对象作为存储,以便为所有调用 scoped_session 注册表的人员维护一个 Session,但仅在单个线程的作用域内。在不同线程中调用注册表的调用者会获得一个 Session 实例,该实例是该其他线程本地的。

使用此技术,scoped_session 提供了一种快速且相对简单(如果熟悉线程局部存储)的方式,在应用程序中提供一个全局对象,该对象可以安全地从多个线程调用。

与往常一样,scoped_session.remove() 方法删除与线程关联的当前 Session(如果有)。但是,threading.local() 对象的一个优点是,如果应用程序线程本身结束,则该线程的“存储”也会被垃圾回收。因此,在生成和销毁线程的应用程序中使用线程局部作用域实际上是“安全的”,而无需调用 scoped_session.remove()。但是,事务本身的作用域,即通过 Session.commit()Session.rollback() 结束事务,通常仍然是必须在适当的时间显式安排的事情,除非应用程序实际上将线程的生命周期与事务的生命周期联系起来。

在 Web 应用程序中使用线程局部作用域

我应该何时构造会话、何时提交会话以及何时关闭会话? 部分所述,Web 应用程序围绕Web 请求的概念构建,将此类应用程序与 Session 集成通常意味着 Session 将与该请求关联。事实证明,大多数 Python Web 框架(值得注意的例外是异步框架 Twisted 和 Tornado)以一种简单的方式使用线程,即在一个特定的 工作线程 的作用域内接收、处理和完成一个特定的 Web 请求。当请求结束时,工作线程被释放到工作线程池,在那里它可以处理另一个请求。

Web 请求和线程的这种简单对应关系意味着,将 Session 与线程关联意味着它也与在该线程中运行的 Web 请求关联,反之亦然,前提是 Session 仅在 Web 请求开始后创建,并在 Web 请求结束前销毁。因此,通常的做法是使用 scoped_session 作为将 Session 与 Web 应用程序集成的快速方法。下面的序列图说明了此流程

Web Server          Web Framework        SQLAlchemy ORM Code
--------------      --------------       ------------------------------
startup        ->   Web framework        # Session registry is established
                    initializes          Session = scoped_session(sessionmaker())

incoming
web request    ->   web request     ->   # The registry is *optionally*
                    starts               # called upon explicitly to create
                                         # a Session local to the thread and/or request
                                         Session()

                                         # the Session registry can otherwise
                                         # be used at any time, creating the
                                         # request-local Session() if not present,
                                         # or returning the existing one
                                         Session.execute(select(MyClass)) # ...

                                         Session.add(some_object) # ...

                                         # if data was modified, commit the
                                         # transaction
                                         Session.commit()

                    web request ends  -> # the registry is instructed to
                                         # remove the Session
                                         Session.remove()

                    sends output      <-
outgoing web    <-
response

使用上述流程,将 Session 与 Web 应用程序集成的过程只有两个要求

  1. 在 Web 应用程序首次启动时创建一个 scoped_session 注册表,确保应用程序的其余部分可以访问此对象。

  2. 确保在 Web 请求结束时调用 scoped_session.remove(),通常通过与 Web 框架的事件系统集成来建立“请求结束时”事件。

如前所述,上述模式只是一种潜在的方式,用于将 Session 与 Web 框架集成,特别是假设 Web 框架将 Web 请求与应用程序线程关联。但是,强烈建议使用 Web 框架本身提供的集成工具(如果可用),而不是 scoped_session

特别是,虽然使用线程局部变量可能很方便,但最好将 Session 直接与请求 而不是与当前线程关联。下一节关于自定义作用域的详细信息介绍了更高级的配置,该配置可以将 scoped_session 的用法与基于直接请求的作用域或任何类型的作用域结合起来。

使用自定义创建的作用域

scoped_session 对象的“线程局部”作用域的默认行为只是如何“作用域” Session 的众多选项之一。可以根据任何现有的获取“我们当前正在处理的事物”的系统来定义自定义作用域。

假设 Web 框架定义了一个库函数 get_current_request()。使用此框架构建的应用程序可以随时调用此函数,结果将是某种 Request 对象,该对象表示当前正在处理的请求。如果 Request 对象是可哈希的,那么此函数可以轻松地与 scoped_session 集成,以将 Session 与请求关联。下面我们说明这一点,并结合 Web 框架 on_request_end 提供的假设事件标记,它允许在每次请求结束时调用代码

from my_web_framework import get_current_request, on_request_end
from sqlalchemy.orm import scoped_session, sessionmaker

Session = scoped_session(sessionmaker(bind=some_engine), scopefunc=get_current_request)


@on_request_end
def remove_session(req):
    Session.remove()

上面,我们以通常的方式实例化 scoped_session,除了我们将返回请求的函数作为“scopefunc”传递。这指示 scoped_session 在每次调用注册表以返回当前 Session 时,使用此函数生成字典键。在这种情况下,尤其重要的是我们要确保实现可靠的“删除”系统,因为此字典没有其他自管理功能。

上下文会话 API

对象名称 描述

QueryPropertyDescriptor

描述应用于类级别 scoped_session.query_property() 属性的类型。

scoped_session

提供 Session 对象的范围管理。

ScopedRegistry

一个注册表,可以根据“作用域”函数存储单个类的一个或多个实例。

ThreadLocalRegistry

一个 ScopedRegistry,它使用 threading.local() 变量进行存储。

class sqlalchemy.orm.scoped_session

提供 Session 对象的范围管理。

有关教程,请参见 上下文/线程局部会话

注意

当使用 异步 I/O (asyncio) 时,应使用异步兼容的 async_scoped_session 类代替 scoped_session

类签名

class sqlalchemy.orm.scoped_session (typing.Generic)

method sqlalchemy.orm.scoped_session.__call__(**kw: Any) _S

返回当前的 Session,如果不存在则使用 scoped_session.session_factory 创建它。

参数:

**kw – 关键字参数将传递给 scoped_session.session_factory 可调用对象,如果不存在现有的 Session。 如果 Session 存在且已传递关键字参数,则会引发 InvalidRequestError 异常。

method sqlalchemy.orm.scoped_session.__init__(session_factory: sessionmaker[_S], scopefunc: Callable[[], Any] | None = None)

构造一个新的 scoped_session

参数:
  • session_factory – 用于创建新的 Session 实例的工厂。 这通常是,但不一定是 sessionmaker 的实例。

  • scopefunc – 可选函数,用于定义当前作用域。 如果未传递,则 scoped_session 对象假定为“线程本地”作用域,并将使用 Python threading.local() 来维护当前的 Session。 如果传递,该函数应返回一个可哈希的令牌; 此令牌将用作字典中的键,以便存储和检索当前的 Session

method sqlalchemy.orm.scoped_session.add(instance: object, _warn: bool = True) None

将对象放入此 Session 中。

代表 scoped_session 类代理 Session 类。

当传递到 Session.add() 方法时,处于 瞬态 状态的对象将移动到 挂起 状态,直到下一次刷新,届时它们将移动到 持久 状态。

当传递到 Session.add() 方法时,处于 分离 状态的对象将直接移动到 持久 状态。

如果 Session 使用的事务回滚,则当传递到 Session.add() 时处于瞬态的对象将移回 瞬态 状态,并且将不再存在于此 Session 中。

method sqlalchemy.orm.scoped_session.add_all(instances: Iterable[object]) None

将给定的实例集合添加到此 Session 中。

代表 scoped_session 类代理 Session 类。

有关一般行为描述,请参阅 Session.add() 的文档。

attribute sqlalchemy.orm.scoped_session.autoflush

代表 scoped_session 类代理 Session.autoflush 属性。

method sqlalchemy.orm.scoped_session.begin(nested: bool = False) SessionTransaction

在此 Session 上开始事务或嵌套事务,如果尚未开始。

代表 scoped_session 类代理 Session 类。

Session 对象具有 autobegin 行为,因此通常不需要显式调用 Session.begin() 方法。 但是,可以使用它来控制事务状态开始的范围。

当用于开始最外层事务时,如果此 Session 已经在一个事务内部,则会引发错误。

参数:

nested – 如果为 True,则开始一个 SAVEPOINT 事务,并且等效于调用 Session.begin_nested()。 有关 SAVEPOINT 事务的文档,请参阅 使用 SAVEPOINT

返回:

SessionTransaction 对象。 请注意,SessionTransaction 充当 Python 上下文管理器,允许在 “with” 块中使用 Session.begin()。 有关示例,请参阅 显式开始

method sqlalchemy.orm.scoped_session.begin_nested() SessionTransaction

在此 Session 上开始“嵌套”事务,例如 SAVEPOINT。

代表 scoped_session 类代理 Session 类。

目标数据库和关联的驱动程序必须支持 SQL SAVEPOINT,此方法才能正常运行。

有关 SAVEPOINT 事务的文档,请参阅 使用 SAVEPOINT

返回:

SessionTransaction 对象。 请注意,SessionTransaction 充当上下文管理器,允许在 “with” 块中使用 Session.begin_nested()。 有关用法示例,请参阅 使用 SAVEPOINT

另请参阅

使用 SAVEPOINT

可序列化隔离/保存点/事务性 DDL - 使用 SQLite 驱动程序需要特殊的解决方法,以使 SAVEPOINT 正常工作。 对于 asyncio 用例,请参阅 可序列化隔离/保存点/事务性 DDL(asyncio 版本) 部分。

attribute sqlalchemy.orm.scoped_session.bind

代表 scoped_session 类代理 Session.bind 属性。

method sqlalchemy.orm.scoped_session.bulk_insert_mappings(mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]], return_defaults: bool = False, render_nulls: bool = False) None

执行给定映射字典列表的批量插入。

代表 scoped_session 类代理 Session 类。

遗留功能

自 SQLAlchemy 2.0 系列起,此方法已成为遗留功能。 对于现代批量 INSERT 和 UPDATE,请参阅 ORM 批量 INSERT 语句ORM 批量 UPDATE(按主键) 部分。 2.0 API 与此方法共享实现细节,并添加了新功能。

参数:
  • mapper – 映射类,或实际的 Mapper 对象,表示映射列表中的单个对象类型。

  • mappings – 字典序列,每个字典都包含要插入的映射行的状态,以映射类上的属性名称表示。 如果映射引用多个表(例如连接继承映射),则每个字典都必须包含要填充到所有表中的所有键。

  • return_defaults

    如果为 True,则 INSERT 过程将更改为确保获取新生成的主键值。 此参数的基本原理通常是为了启用 连接表继承 映射以进行批量插入。

    注意

    对于不支持 RETURNING 的后端,Session.bulk_insert_mappings.return_defaults 参数会显着降低性能,因为 INSERT 语句不再能批量处理。 有关哪些后端受到影响的背景信息,请参阅 INSERT 语句的“插入多个值”行为

  • render_nulls

    如果为 True,则 None 值将导致在 INSERT 语句中包含 NULL 值,而不是从 INSERT 中省略列。 这允许所有正在 INSERT 的行具有相同的列集,从而允许将整组行批量处理到 DBAPI。 通常,每个包含与前一行不同的 NULL 值组合的列集必须从呈现的 INSERT 语句中省略一系列不同的列,这意味着它必须作为单独的语句发出。 通过传递此标志,可以保证整组行可以批量处理到一个批次中; 但是,代价是会跳过由省略的列调用的服务器端默认值,因此必须注意确保这些不是必需的。

    警告

    设置此标志后,服务器端默认 SQL 值将不会为那些作为 NULL 插入的列调用; NULL 值将被显式发送。 必须注意确保不需要为整个操作调用任何服务器端默认函数。

method sqlalchemy.orm.scoped_session.bulk_save_objects(objects: Iterable[object], return_defaults: bool = False, update_changed_only: bool = True, preserve_order: bool = True) None

执行给定对象列表的批量保存。

代表 scoped_session 类代理 Session 类。

遗留功能

自 SQLAlchemy 2.0 系列起,此方法已成为遗留功能。 对于现代批量 INSERT 和 UPDATE,请参阅 ORM 批量 INSERT 语句ORM 批量 UPDATE(按主键) 部分。

对于现有 ORM 映射对象的一般 INSERT 和 UPDATE,请首选 工作单元 数据管理模式,这些模式在 SQLAlchemy 统一教程使用 ORM 进行数据操作 中介绍。 SQLAlchemy 2.0 现在将 INSERT 语句的“插入多个值”行为 与现代方言结合使用,这解决了以前批量 INSERT 速度慢的问题。

参数:
  • objects

    映射对象实例的序列。 映射对象按原样持久化,并且与之后的 Session 关联。

    对于每个对象,对象是作为 INSERT 还是 UPDATE 发送取决于 Session 在传统操作中使用的相同规则; 如果对象设置了 InstanceState.key 属性,则假定该对象为“分离”状态,并将导致 UPDATE。 否则,将使用 INSERT。

    在 UPDATE 的情况下,语句根据已更改的属性进行分组,因此这些属性将成为每个 SET 子句的主题。 如果 update_changed_only 为 False,则每个对象中存在的所有属性都将应用于 UPDATE 语句,这可能有助于将语句分组到更大的 executemany() 中,并且还将减少检查属性历史记录的开销。

  • return_defaults – 如果为 True,则缺少生成默认值的行(即整数主键默认值和序列)将一次插入一个,以便主键值可用。 特别是,这将允许连接继承和其他多表映射正确插入,而无需预先提供主键值; 但是,Session.bulk_save_objects.return_defaults大大降低该方法整体的性能提升。 强烈建议使用标准的 Session.add_all() 方法。

  • update_changed_only – 如果为 True,则 UPDATE 语句根据每个状态中已记录更改的那些属性呈现。 如果为 False,则除主键属性外,所有存在的属性都将呈现到 SET 子句中。

  • preserve_order – 如果为 True,则插入和更新的顺序与给定对象的顺序完全匹配。 如果为 False,则常见类型的对象将分组为插入和更新,以便有更多的批量处理机会。

method sqlalchemy.orm.scoped_session.bulk_update_mappings(mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]]) None

执行给定映射字典列表的批量更新。

代表 scoped_session 类代理 Session 类。

遗留功能

自 SQLAlchemy 2.0 系列起,此方法已成为遗留功能。 对于现代批量 INSERT 和 UPDATE,请参阅 ORM 批量 INSERT 语句ORM 批量 UPDATE(按主键) 部分。 2.0 API 与此方法共享实现细节,并添加了新功能。

参数:
  • mapper – 映射类,或实际的 Mapper 对象,表示映射列表中的单个对象类型。

  • mappings – 字典序列,每个字典都包含要更新的映射行的状态,以映射类上的属性名称表示。 如果映射引用多个表(例如连接继承映射),则每个字典可能包含对应于所有表的键。 所有存在的且不是主键一部分的键都将应用于 UPDATE 语句的 SET 子句; 主键值(必需)将应用于 WHERE 子句。

method sqlalchemy.orm.scoped_session.close() None

关闭此 Session 使用的事务资源和 ORM 对象。

代表 scoped_session 类代理 Session 类。

这将清除与此 Session 关联的所有 ORM 对象,结束任何正在进行的事务,并释放Session 本身已从关联的 Engine 对象中检出的任何 Connection 对象。 然后,该操作使 Session 处于可以再次使用的状态。

提示

在默认运行模式下,Session.close() 方法不会阻止 Session 再次使用Session 本身实际上没有明显的“关闭”状态; 它仅表示 Session 将释放所有数据库连接和 ORM 对象。

将参数 Session.close_resets_only 设置为 False 将使 close 变为最终状态,这意味着将禁止对会话执行任何进一步的操作。

在版本 1.4 中更改: Session.close() 方法不会立即创建新的 SessionTransaction 对象; 相反,只有当 Session 再次用于数据库操作时,才会创建新的 SessionTransaction

另请参阅

关闭 - 有关 Session.close()Session.reset() 语义的详细信息。

Session.reset() - 类似于 close() 的方法,参数 Session.close_resets_only 设置为 True

类方法 sqlalchemy.orm.scoped_session.close_all() None

关闭内存中的所有会话。

代表 scoped_session 类代理 Session 类。

Deprecated since version 1.3: 自版本 1.3 弃用: The Session.close_all() method is deprecated and will be removed in a future release. Please refer to close_all_sessions(). 请参考 close_all_sessions()

方法 sqlalchemy.orm.scoped_session.commit() None

刷新待处理的更改并提交当前事务。

代表 scoped_session 类代理 Session 类。

当 COMMIT 操作完成时,所有对象都完全过期,擦除其内部内容,这些内容将在下次访问对象时自动重新加载。在此期间,这些对象处于过期状态,如果它们与Session分离,则它们将无法正常工作。此外,当使用面向 asyncio 的 API 时,不支持此重新加载操作。Session.expire_on_commit 参数可用于禁用此行为。

Session 没有事务时,表示自上次调用 Session.commit() 以来,此 Session 上未调用任何操作时,该方法将开始并提交一个仅限内部使用的“逻辑”事务,除非检测到待处理的刷新更改,否则通常不会影响数据库,但仍会调用事件处理程序和对象过期规则。

最外层的数据库事务将被无条件地提交,自动释放任何生效的 SAVEPOINT。

方法 sqlalchemy.orm.scoped_session.configure(**kwargs: Any) None

重新配置此 scoped_session 使用的 sessionmaker

请参阅 sessionmaker.configure()

方法 sqlalchemy.orm.scoped_session.connection(bind_arguments: _BindArguments | None = None, execution_options: CoreExecuteOptionsParameter | None = None) Connection

返回与此 Session 对象的事务状态相对应的 Connection 对象。

代表 scoped_session 类代理 Session 类。

返回与当前事务对应的 Connection,或者如果没有正在进行的事务,则开始一个新的事务并返回 Connection(请注意,在发出第一个 SQL 语句之前,不会与 DBAPI 建立事务状态)。

多绑定或未绑定的 Session 对象中的歧义可以通过任何可选的关键字参数来解决。这最终使得 get_bind() 方法可用于解析。

参数:
方法 sqlalchemy.orm.scoped_session.delete(instance: object) None

将实例标记为已删除。

代表 scoped_session 类代理 Session 类。

假定传递的对象是持久分离的;在调用该方法后,该对象将保持持久状态,直到下一次刷新继续进行。在此期间,该对象也将是 Session.deleted 集合的成员。

当下一次刷新继续进行时,该对象将移动到已删除状态,表明为其行发出了 DELETE 语句在当前事务中。当事务成功提交时,已删除的对象将移动到分离状态,并且不再存在于此 Session 中。

另请参阅

删除 - 在 使用会话的基础知识

属性 sqlalchemy.orm.scoped_session.deleted

Session 中标记为“已删除”的所有实例的集合

代表 scoped_session 类代理 Session 类。

属性 sqlalchemy.orm.scoped_session.dirty

被认为是脏数据的所有持久实例的集合。

代表 scoped_session 类代理 Session 类。

例如:

some_mapped_object in session.dirty

实例在被修改但未被删除时被认为是脏数据。

请注意,此“脏数据”计算是“乐观的”;大多数属性设置或集合修改操作都会将实例标记为“脏数据”并将其放入此集合中,即使属性的值没有净变化。在刷新时,每个属性的值都会与其先前保存的值进行比较,如果没有任何净变化,则不会发生 SQL 操作(这是一个更昂贵的操作,因此仅在刷新时完成)。

要检查实例的属性是否具有可操作的净更改,请使用 Session.is_modified() 方法。

方法 sqlalchemy.orm.scoped_session.execute(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, _parent_execute_state: Any | None = None, _add_event: Any | None = None) Result[Any]

执行 SQL 表达式构造。

代表 scoped_session 类代理 Session 类。

返回一个 Result 对象,表示语句执行的结果。

例如:

from sqlalchemy import select

result = session.execute(select(User).where(User.id == 5))

Session.execute() 的 API 约定类似于 Connection.execute()2.0 风格 版本的 Connection

Changed in version 1.4: 自版本 1.4 更改: 当使用 2.0 风格 ORM 用法时,Session.execute() 方法现在是 ORM 语句执行的主要入口点。

参数:
  • statement – 可执行语句(即 Executable 表达式,例如 select())。

  • params – 可选字典或字典列表,包含绑定参数值。如果是一个字典,则执行单行;如果是字典列表,则将调用“executemany”。每个字典中的键必须与语句中存在的参数名称相对应。

  • execution_options

    可选的执行选项字典,它将与语句执行关联。此字典可以提供 Connection.execution_options() 接受的选项子集,并且还可以提供仅在 ORM 上下文中理解的其他选项。

    另请参阅

    ORM 执行选项 - ORM 特定的执行选项

  • bind_arguments – 用于确定绑定的其他参数的字典。可能包括 “mapper”、“bind” 或其他自定义参数。此字典的内容将传递给 Session.get_bind() 方法。

返回:

一个 Result 对象。

方法 sqlalchemy.orm.scoped_session.expire(instance: object, attribute_names: Iterable[str] | None = None) None

使实例上的属性过期。

代表 scoped_session 类代理 Session 类。

将实例的属性标记为过时。当下一次访问过期的属性时,将向 Session 对象的当前事务上下文中发出查询,以便为给定实例加载所有过期的属性。请注意,高度隔离的事务将返回与同一事务中先前读取的值相同的值,而与该事务之外的数据库状态更改无关。

要同时使 Session 中的所有对象过期,请使用 Session.expire_all()

Session 对象的默认行为是在调用 Session.rollback()Session.commit() 方法时,使所有状态过期,以便可以为新事务加载新状态。因此,仅在当前事务中发出了非 ORM SQL 语句的特定情况下,调用 Session.expire() 才有意义。

参数:
  • instance – 要刷新的实例。

  • attribute_names – 可选的字符串属性名称列表,指示要过期的属性子集。

方法 sqlalchemy.orm.scoped_session.expire_all() None

使此 Session 中的所有持久实例过期。

代表 scoped_session 类代理 Session 类。

当下一次访问持久实例上的任何属性时,将使用 Session 对象的当前事务上下文发出查询,以便为给定实例加载所有过期的属性。请注意,高度隔离的事务将返回与同一事务中先前读取的值相同的值,而与该事务之外的数据库状态更改无关。

要使单个对象和这些对象上的单个属性过期,请使用 Session.expire()

Session 对象的默认行为是在调用 Session.rollback()Session.commit() 方法时,使所有状态过期,以便可以为新事务加载新状态。因此,假设事务是隔离的,通常不需要调用 Session.expire_all()

方法 sqlalchemy.orm.scoped_session.expunge(instance: object) None

从此 Session 中移除 instance

代表 scoped_session 类代理 Session 类。

这将释放对实例的所有内部引用。级联将根据 expunge 级联规则应用。

方法 sqlalchemy.orm.scoped_session.expunge_all() None

从此 Session 中移除所有对象实例。

代表 scoped_session 类代理 Session 类。

这等效于对这个 Session 中的所有对象调用 expunge(obj)

方法 sqlalchemy.orm.scoped_session.flush(objects: Sequence[Any] | None = None) None

将所有对象更改刷新到数据库。

代表 scoped_session 类代理 Session 类。

将所有待处理的对象创建、删除和修改作为 INSERT、DELETE、UPDATE 等写入数据库。操作由 Session 的工作单元依赖关系求解器自动排序。

数据库操作将在当前事务上下文中发出,并且不影响事务的状态,除非发生错误,在这种情况下,整个事务将回滚。您可以在事务中根据需要经常调用 flush(),以将更改从 Python 移动到数据库的事务缓冲区。

参数:

objects

可选;将刷新操作限制为仅对给定集合中的元素进行操作。

此功能适用于非常狭窄的一组用例,在这些用例中,可能需要在完全 flush() 发生之前对特定对象进行操作。它不适用于一般用途。

method sqlalchemy.orm.scoped_session.get(entity: _EntityBindKey[_O], ident: _PKIdentityArgument, *, options: Sequence[ORMOption] | None = None, populate_existing: bool = False, with_for_update: ForUpdateParameter = None, identity_token: Any | None = None, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None) _O | None

根据给定的主键标识符返回一个实例,如果未找到则返回 None

代表 scoped_session 类代理 Session 类。

例如:

my_user = session.get(User, 5)

some_object = session.get(VersionedFoo, (5, 10))

some_object = session.get(VersionedFoo, {"id": 5, "version_id": 10})

1.4 版本新增: 添加了 Session.get() 方法,该方法从现在已过时的 Query.get() 方法中移出。

Session.get() 的特殊之处在于它提供了对 Session 的 identity map 的直接访问。如果给定的主键标识符存在于本地 identity map 中,则会直接从该集合返回对象,并且不会发出 SQL,除非该对象已被标记为完全过期。如果不存在,则会执行 SELECT 以查找对象。

Session.get() 还会执行检查,以查看对象是否在 identity map 中并且标记为过期 - 将发出 SELECT 以刷新对象,并确保该行仍然存在。如果不存在,则会引发 ObjectDeletedError 异常。

参数:
  • entity – 一个映射类或 Mapper,指示要加载的实体类型。

  • ident

    表示主键的标量、元组或字典。对于复合(例如多列)主键,应传递元组或字典。

    对于单列主键,标量调用形式通常是最方便的。如果行的主键值为“5”,则调用如下所示:

    my_object = session.get(SomeClass, 5)

    元组形式包含主键值,通常按照它们对应于映射的 Table 对象的主键列的顺序,或者如果使用了 Mapper.primary_key 配置参数,则按照该参数使用的顺序。例如,如果行的主键由整数数字“5, 10”表示,则调用将如下所示:

    my_object = session.get(SomeClass, (5, 10))

    字典形式应包含作为键的映射属性名称,这些名称对应于主键的每个元素。如果映射类具有属性 idversion_id 作为存储对象主键值的属性,则调用将如下所示:

    my_object = session.get(SomeClass, {"id": 5, "version_id": 10})

  • options – 可选的加载器选项序列,如果发出查询,则将应用于该查询。

  • populate_existing – 使该方法无条件地发出 SQL 查询并使用新加载的数据刷新对象,无论该对象是否已存在。

  • with_for_update – 可选的布尔值 True,指示应使用 FOR UPDATE,或者可以是包含标志的字典,以指示更具体的 FOR UPDATE 标志集用于 SELECT;标志应与 Query.with_for_update() 的参数匹配。取代 Session.refresh.lockmode 参数。

  • execution_options

    可选的执行选项字典,如果发出查询,则将与查询执行关联。此字典可以提供 Connection.execution_options() 接受的选项的子集,并且还可以提供仅在 ORM 上下文中理解的其他选项。

    1.4.29 版本新增。

    另请参阅

    ORM 执行选项 - ORM 特定的执行选项

  • bind_arguments

    用于确定绑定的附加参数字典。可以包括 “mapper”、“bind” 或其他自定义参数。此字典的内容将传递给 Session.get_bind() 方法。

返回:

对象实例,或 None

method sqlalchemy.orm.scoped_session.get_bind(mapper: _EntityBindKey[_O] | None = None, *, clause: ClauseElement | None = None, bind: _SessionBind | None = None, _sa_skip_events: bool | None = None, _sa_skip_for_implicit_returning: bool = False, **kw: Any) Engine | Connection

返回此 Session 绑定的 “bind”。

代表 scoped_session 类代理 Session 类。

“bind” 通常是 Engine 的实例,除非 Session 已显式直接绑定到 Connection

对于多重绑定或未绑定的 Sessionmapperclause 参数用于确定要返回的适当绑定。

请注意,当通过 ORM 操作(例如 Session.query()Session.flush() 调用中的每个单独的 INSERT/UPDATE/DELETE 操作等)调用 Session.get_bind() 时,“mapper” 参数通常会存在。

解析顺序如下:

  1. 如果给定了 mapper 且存在 Session.binds,则首先根据正在使用的 mapper、然后根据正在使用的映射类、最后根据映射类的 __mro__ 中存在的任何基类(从更具体的超类到更一般的超类)来查找绑定。

  2. 如果给定了 clause 且存在 Session.binds,则根据 Session.binds 中存在的给定 clause 中找到的 Table 对象来查找绑定。

  3. 如果存在 Session.binds,则返回该绑定。

  4. 如果给定了 clause,则尝试返回链接到最终与 clause 关联的 MetaData 的绑定。

  5. 如果给定了 mapper,则尝试返回链接到最终与 mapper 映射到的 Table 或其他 selectable 关联的 MetaData 的绑定。

  6. 如果找不到绑定,则会引发 UnboundExecutionError 异常。

请注意,可以在 Session 的用户定义的子类上重写 Session.get_bind() 方法,以提供任何类型的绑定解析方案。请参阅 自定义垂直分区 中的示例。

参数:
  • mapper – 可选的映射类或相应的 Mapper 实例。可以通过首先查询与此 Session 关联的 “binds” 映射,其次查询与 Mapper 映射到的 Table 关联的 MetaData 来从 Mapper 派生绑定。

  • clauseClauseElement (即 select(), text() 等)。如果 mapper 参数不存在或无法生成绑定,则将在给定的表达式构造中搜索绑定元素,通常是与绑定 MetaData 关联的 Table

method sqlalchemy.orm.scoped_session.get_one(entity: _EntityBindKey[_O], ident: _PKIdentityArgument, *, options: Sequence[ORMOption] | None = None, populate_existing: bool = False, with_for_update: ForUpdateParameter = None, identity_token: Any | None = None, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None) _O

根据给定的主键标识符返回 *恰好一个* 实例,如果未找到则引发异常。

代表 scoped_session 类代理 Session 类。

如果查询未选择任何行,则引发 sqlalchemy.orm.exc.NoResultFound 异常。

有关参数的详细文档,请参阅 Session.get() 方法。

2.0.22 版本新增。

返回:

对象实例。

另请参阅

Session.get() - 等效方法,但如果未找到具有提供的主键的行,则

返回 None

classmethod sqlalchemy.orm.scoped_session.identity_key(class_: Type[Any] | None = None, ident: Any | Tuple[Any, ...] = None, *, instance: Any | None = None, row: Row[Any] | RowMapping | None = None, identity_token: Any | None = None) _IdentityKeyType[Any]

返回一个 identity key。

代表 scoped_session 类代理 Session 类。

这是 identity_key() 的别名。

attribute sqlalchemy.orm.scoped_session.identity_map

代表 scoped_session 类代理 Session.identity_map 属性。

attribute sqlalchemy.orm.scoped_session.info

用户可修改的字典。

代表 scoped_session 类代理 Session 类。

可以使用 info 参数来填充此字典的初始值,该参数可用于 Session 构造函数或 sessionmaker 构造函数或工厂方法。info 字典始终是此 Session 本地的,并且可以独立于所有其他 Session 对象进行修改。

attribute sqlalchemy.orm.scoped_session.is_active

如果此 Session 未处于 “partial rollback” 状态,则为 True。

代表 scoped_session 类代理 Session 类。

在 1.4 版本中变更: Session 不再立即开始新的事务,因此当首次实例化 Session 时,此属性将为 False。

“partial rollback” 状态通常表示 Session 的 flush 进程失败,并且必须发出 Session.rollback() 方法才能完全回滚事务。

如果此 Session 根本不在事务中,则 Session 将在首次使用时自动开始事务,因此在这种情况下 Session.is_active 将返回 True。

否则,如果此 Session 处于事务中,并且该事务尚未在内部回滚,则 Session.is_active 也将返回 True。

method sqlalchemy.orm.scoped_session.is_modified(instance: object, include_collections: bool = True) bool

如果给定的实例具有本地修改的属性,则返回 True

代表 scoped_session 类代理 Session 类。

此方法检索实例上每个已检测属性的历史记录,并将当前值与其先前刷新或提交的值(如果有)进行比较。

实际上,它是一种更昂贵但更准确的检查给定实例是否在 Session.dirty 集合中的方法;将对每个属性的网络“脏”状态执行完整测试。

例如:

return session.is_modified(someobject)

此方法有一些注意事项适用

  • Session.dirty 集合中存在的实例在使用此方法测试时可能会报告 False。这是因为对象可能已通过属性突变接收到更改事件,从而将其置于 Session.dirty 中,但最终状态与从数据库加载的状态相同,因此此处没有净更改。

  • 当应用新值时,标量属性可能未记录先前设置的值,如果该属性未加载或已过期 - 在这些情况下,即使最终相对于其数据库值没有净更改,也假定该属性已更改。在大多数情况下,SQLAlchemy 在发生设置事件时不需要“旧”值,因此如果旧值不存在,则会跳过 SQL 调用的开销,这是基于通常需要更新标量值的假设,并且在少数不需要更新的情况下,平均而言,其开销低于发出防御性 SELECT。

    仅当属性容器的 active_history 标志设置为 True 时,才会在设置时无条件地获取“旧”值。此标志通常为主键属性和非简单多对一的标量对象引用设置。要为任何任意映射列设置此标志,请将 active_history 参数与 column_property() 一起使用。

参数:
  • instance – 要测试待定更改的映射实例。

  • include_collections – 指示多值集合是否应包含在操作中。将此设置为 False 是一种仅检测本地列属性(即标量列或多对一外键)的方法,这些属性将在 flush 时导致此实例的 UPDATE。

method sqlalchemy.orm.scoped_session.merge(instance: _O, *, load: bool = True, options: Sequence[ORMOption] | None = None) _O

将给定实例的状态复制到此 Session 中的对应实例。

代表 scoped_session 类代理 Session 类。

Session.merge() 检查源实例的主键属性,并尝试将其与会话中具有相同主键的实例进行协调。如果本地未找到,它会尝试根据主键从数据库加载对象,如果找不到,则创建新实例。然后,源实例上每个属性的状态都会复制到目标实例。然后,该方法返回生成的目标实例;原始源实例保持不变,并且如果尚未与 Session 关联,则保持未关联状态。

如果关联映射了 cascade="merge",则此操作会级联到关联的实例。

有关合并的详细讨论,请参阅 合并

参数:
  • instance – 要合并的实例。

  • load

    布尔值,当为 False 时,merge() 切换到“高性能”模式,这将导致它放弃发出历史记录事件以及所有数据库访问。此标志用于将对象图传输到 Session(从二级缓存),或将刚加载的对象传输到工作线程或进程拥有的 Session,而无需重新查询数据库等情况。

    load=False 用例增加了一个警告,即给定的对象必须处于“干净”状态,即没有待刷新的更改 - 即使传入的对象与任何 Session 分离。这是为了当合并操作填充本地属性并级联到相关对象和集合时,可以将值“盖印”到目标对象上,而无需生成任何历史记录或属性事件,也无需将传入数据与可能未加载的任何现有相关对象或集合进行协调。load=False 生成的结果对象始终是“干净”的,因此给定的对象也应该是“干净”的,否则表明该方法被误用。

  • options

    加载器选项的可选序列,当合并操作从数据库加载对象的现有版本时,将应用于 Session.get() 方法。

    1.4.24 版本新增功能。

另请参阅

make_transient_to_detached() - 提供了一种将单个对象“合并”到 Session 的替代方法

attribute sqlalchemy.orm.scoped_session.new

在此 Session 中标记为“new”的所有实例的集合。

代表 scoped_session 类代理 Session 类。

attribute sqlalchemy.orm.scoped_session.no_autoflush

返回禁用自动刷新的上下文管理器。

代表 scoped_session 类代理 Session 类。

例如:

with session.no_autoflush:

    some_object = SomeClass()
    session.add(some_object)
    # won't autoflush
    some_object.related_thing = session.query(SomeRelated).first()

with: 块中进行的操作将不受查询访问时发生的刷新的影响。当初始化一系列涉及现有数据库查询的对象时,这非常有用,此时不应刷新未完成的对象。

classmethod sqlalchemy.orm.scoped_session.object_session(instance: object) Session | None

返回对象所属的 Session

代表 scoped_session 类代理 Session 类。

这是 object_session() 的别名。

method sqlalchemy.orm.scoped_session.query(*entities: _ColumnsClauseArgument[Any], **kwargs: Any) Query[Any]

返回与此 Session 对应的新 Query 对象。

代表 scoped_session 类代理 Session 类。

请注意,Query 对象在 SQLAlchemy 2.0 中已过时;select() 构造现在用于构造 ORM 查询。

另请参阅

SQLAlchemy 统一教程

ORM 查询指南

旧版 Query API - 旧版 API 文档

method sqlalchemy.orm.scoped_session.query_property(query_cls: Type[Query[_T]] | None = None) QueryPropertyDescriptor

返回一个类属性,该属性在调用时针对类和当前 Session 生成旧版 Query 对象。

遗留功能

scoped_session.query_property() 访问器特定于旧版 Query 对象,并且不被认为是 2.0 风格 ORM 用法的一部分。

例如:

from sqlalchemy.orm import QueryPropertyDescriptor
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

Session = scoped_session(sessionmaker())


class MyClass:
    query: QueryPropertyDescriptor = Session.query_property()


# after mappers are defined
result = MyClass.query.filter(MyClass.name == "foo").all()

默认情况下,生成会话的已配置查询类的实例。要覆盖并使用自定义实现,请提供 query_cls 可调用对象。将使用类的 mapper 作为位置参数和 session 关键字参数来调用该可调用对象。

放置在类上的 query 属性的数量没有限制。

method sqlalchemy.orm.scoped_session.refresh(instance: object, attribute_names: Iterable[str] | None = None, with_for_update: ForUpdateParameter = None) None

使给定实例上的属性过期并刷新。

代表 scoped_session 类代理 Session 类。

选定的属性将首先像使用 Session.expire() 时一样过期;然后将向数据库发出 SELECT 语句,以使用当前事务中可用的当前值刷新面向列的属性。

relationship() 定向的属性如果已在对象上急切加载,也将立即加载,使用与最初加载时相同的急切加载策略。

1.4 版本新增功能:- Session.refresh() 方法还可以刷新急切加载的属性。

通常使用 select(或“lazy”)加载器策略加载的 relationship() 定向属性也将加载,如果它们在 attribute_names 集合中显式命名,则使用 immediate 加载器策略为该属性发出 SELECT 语句。如果延迟加载的关系未在 Session.refresh.attribute_names 中命名,则它们仍然是“延迟加载”属性,并且不会隐式刷新。

2.0.4 版本更改:Session.refresh() 方法现在将刷新 relationship() 定向属性的延迟加载,这些属性在 Session.refresh.attribute_names 集合中显式命名。

提示

虽然 Session.refresh() 方法能够刷新面向列和面向关系的属性,但其主要重点是刷新单个实例上的本地面向列的属性。对于更开放的“刷新”功能,包括一次刷新多个对象上的属性,同时显式控制关系加载器策略的能力,请改用 populate existing 功能。

请注意,高度隔离的事务将返回与同一事务中先前读取的值相同的值,而与该事务之外的数据库状态更改无关。刷新属性通常仅在尚未访问数据库行的事务开始时才有意义。

参数:
  • attribute_names – 可选。字符串属性名称的可迭代集合,指示要刷新的属性子集。

  • with_for_update – 可选布尔值 True,指示应使用 FOR UPDATE,或者可以是包含标志的字典,以指示 SELECT 的更具体的 FOR UPDATE 标志集;标志应与 Query.with_for_update() 的参数匹配。取代 Session.refresh.lockmode 参数。

另请参阅

刷新/过期 - 介绍性材料

Session.expire()

Session.expire_all()

Populate Existing - 允许任何 ORM 查询刷新对象,就像它们通常被加载一样。

method sqlalchemy.orm.scoped_session.remove() None

如果存在,则释放当前的 Session

这将首先对当前的 Session 调用 Session.close() 方法,这将释放任何仍持有的现有事务/连接资源;特别是事务将被回滚。Session 随后将被丢弃。在同一范围内下次使用时,scoped_session 将生成一个新的 Session 对象。

method sqlalchemy.orm.scoped_session.reset() None

关闭此 Session 使用的事务资源和 ORM 对象,将 session 重置为其初始状态。

代表 scoped_session 类代理 Session 类。

此方法提供了与 Session.close() 方法历史上提供的相同的“仅重置”行为,其中 Session 的状态被重置,就好像该对象是全新的,并且准备再次使用一样。然后,此方法可能适用于将 Session.close_resets_only 设置为 FalseSession 对象,以便“仅重置”行为仍然可用。

2.0.22 版本新增。

另请参阅

关闭 - 有关 Session.close()Session.reset() 语义的详细信息。

Session.close() - 当参数 Session.close_resets_only 设置为 False 时,类似的方法将额外阻止 Session 的重用。

方法 sqlalchemy.orm.scoped_session.rollback() None

回滚当前正在进行的事务。

代表 scoped_session 类代理 Session 类。

如果没有正在进行的事务,此方法将直接跳过。

该方法始终回滚最顶层的数据库事务,丢弃任何可能正在进行的嵌套事务。

另请参阅

回滚

管理事务

方法 sqlalchemy.orm.scoped_session.scalar(statement: Executable, params: _CoreSingleExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) Any

执行一个语句并返回一个标量结果。

代表 scoped_session 类代理 Session 类。

用法和参数与 Session.execute() 相同; 返回结果是一个标量 Python 值。

方法 sqlalchemy.orm.scoped_session.scalars(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) ScalarResult[Any]

执行一个语句并将结果作为标量返回。

代表 scoped_session 类代理 Session 类。

用法和参数与 Session.execute() 相同; 返回结果是一个 ScalarResult 过滤对象,它将返回单个元素而不是 Row 对象。

返回:

一个 ScalarResult 对象

1.4.24 版本新增: 添加了 Session.scalars()

1.4.26 版本新增: 添加了 scoped_session.scalars()

另请参阅

选择 ORM 实体 - 对比了 Session.execute()Session.scalars() 的行为

属性 sqlalchemy.orm.scoped_session.session_factory: sessionmaker[_S]

提供给 __init__session_factory 存储在此属性中,并可在稍后访问。当需要新的非作用域 Session 时,这可能很有用。

sqlalchemy.util.ScopedRegistry

一个注册表,可以根据“作用域”函数存储单个类的一个或多个实例。

该对象将 __call__ 实现为 “getter”,因此通过调用 myregistry() 将返回当前作用域的包含对象。

参数:
  • createfunc – 一个可调用对象,返回一个要放入注册表的新对象

  • scopefunc – 一个可调用对象,它将返回一个键来存储/检索对象。

类签名

sqlalchemy.util.ScopedRegistry (typing.Generic)

方法 sqlalchemy.util.ScopedRegistry.__init__(createfunc: Callable[[], _T], scopefunc: Callable[[], Any])

构造一个新的 ScopedRegistry

参数:
  • createfunc – 一个创建函数,如果当前作用域中不存在值,它将为当前作用域生成一个新值。

  • scopefunc – 一个函数,它返回一个可哈希的令牌,表示当前作用域(例如,当前线程标识符)。

方法 sqlalchemy.util.ScopedRegistry.clear() None

清除当前作用域(如果存在)。

方法 sqlalchemy.util.ScopedRegistry.has() bool

如果当前作用域中存在对象,则返回 True。

方法 sqlalchemy.util.ScopedRegistry.set(obj: _T) None

设置当前作用域的值。

sqlalchemy.util.ThreadLocalRegistry

一个 ScopedRegistry,它使用 threading.local() 变量进行存储。

sqlalchemy.orm.QueryPropertyDescriptor

描述应用于类级别 scoped_session.query_property() 属性的类型。

2.0.5 版本新增。

类签名

sqlalchemy.orm.QueryPropertyDescriptor (typing_extensions.Protocol)