SqlAlchemy-2-0-中文文档-十-

SqlAlchemy 2.0 中文文档(十)

原文:docs.sqlalchemy.org/en/20/contents.html

传统查询 API

原文:docs.sqlalchemy.org/en/20/orm/queryguide/query.html

关于传统查询 API

本页包含了由 Python 生成的Query构造的文档,多年来这是与 SQLAlchemy ORM 一起使用时的唯一 SQL 接口。从版本 2.0 开始,现在采用的是全新的工作方式,其中与 Core 相同的select()构造对 ORM 同样有效,为构建查询提供了一致的接口。

对于在 SQLAlchemy 2.0 API 之前构建的任何应用程序,Query API 通常表示应用程序中绝大多数数据库访问代码,并且大部分Query API 不会从 SQLAlchemy 中删除。在执行Query对象时,Query对象在幕后现在会将自己转换为 2.0 样式的select()对象,因此现在它只是一个非常薄的适配器 API。

要了解如何将基于Query的应用程序迁移到 2.0 样式,请参阅 2.0 迁移 - ORM 用法。

要了解如何以 2.0 样式编写 ORM 对象的 SQL,请从 SQLAlchemy 统一教程开始。2.0 样式查询的其他参考资料请参阅 ORM 查询指南。

查询对象

Query是根据给定的Session产生的,使用Session.query()方法:

q = session.query(SomeMappedClass)

以下是Query对象的完整接口。

对象名称 描述
查询 ORM 级别的 SQL 构造对象。
class sqlalchemy.orm.Query

ORM 级别的 SQL 构造对象。

传统特性

ORM Query对象是 SQLAlchemy 2.0 的传统构造。请参阅传统查询 API 顶部的注释,其中包括迁移文档的链接。

查询 对象通常最初是使用 Session.query() 方法生成的,Session 的情况比较少是直接实例化 Query 并使用 Query.with_session() 方法与 Session 关联。

成员

init(), add_column(), add_columns(), add_entity(), all(), apply_labels(), as_scalar(), autoflush(), column_descriptions, correlate(), count(), cte(), delete(), distinct(), enable_assertions(), enable_eagerloads(), except_(), except_all(), execution_options(), exists(), filter(), filter_by(), first(), from_statement(), get(), get_children(), get_execution_options(), get_label_style, group_by(), having(), instances(), intersect(), intersect_all(), is_single_entity, join(), label(), lazy_loaded_from, limit(), merge_result(), offset(), one(), one_or_none(), only_return_tuples(), options(), order_by(), outerjoin(), params(), populate_existing(), prefix_with(), reset_joinpoint(), scalar(), scalar_subquery(), select_from(), selectable, set_label_style(), slice(), statement, subquery(), suffix_with(), tuples(), union(), union_all(), update(), value(), values(), where(), whereclause, with_entities(), with_for_update(), with_hint(), with_labels(), with_parent(), with_session(), with_statement_hint(), with_transformation(), yield_per()

类签名

sqlalchemy.orm.Querysqlalchemy.sql.expression._SelectFromElementssqlalchemy.sql.annotation.SupportsCloneAnnotationssqlalchemy.sql.expression.HasPrefixessqlalchemy.sql.expression.HasSuffixessqlalchemy.sql.expression.HasHintssqlalchemy.event.registry.EventTargetsqlalchemy.log.Identifiedsqlalchemy.sql.expression.Generativesqlalchemy.sql.expression.Executabletyping.Generic

method __init__(entities: _ColumnsClauseArgument[Any] | Sequence[_ColumnsClauseArgument[Any]], session: Session | None = None)

直接构造一个Query

例如:

q = Query([User, Address], session=some_session)

以上等价于:

q = some_session.query(User, Address)

参数:

  • entities – 一个实体和/或 SQL 表达式的序列。

  • session – 与Query将关联的Session。可选;也可以通过Query.with_session()方法将QuerySession关联。

另请参见

Session.query()

Query.with_session()

method add_column(column: _ColumnExpressionArgument[Any]) → Query[Any]

将列表达式添加到要返回的结果列列表中。

自版本 1.4 起已弃用:Query.add_column()已弃用,并将在将来的版本中删除。请使用 Query.add_columns()

method add_columns(*column: _ColumnExpressionArgument[Any]) → Query[Any]

将一个或多个列表达式添加到要返回的结果列列表中。

另请参见

Select.add_columns() - v2 可比较的方法。

method add_entity(entity: _EntityType[Any], alias: Alias | Subquery | None = None) → Query[Any]

将映射实体添加到要返回的结果列列表中。

另请参见

Select.add_columns() - v2 可比较的方法。

method all() → List[_T]

将由此Query表示的结果返回为列表。

这将导致底层 SQL 语句的执行。

警告

当要求 Query 对象返回由完整的 ORM 映射实体组成的序列或迭代器时,将根据主键对条目进行去重。有关更多详情,请参阅 FAQ。

另请参阅

我的查询返回的对象数量与 query.count() 告诉我的数量不一致 - 为什么?

另请参阅

Result.all() - v2 可比较方法。

Result.scalars() - v2 可比较方法。

method apply_labels() → Self

自版本 2.0 弃用:Query.with_labels()Query.apply_labels() 方法被视为 SQLAlchemy 1.x 系列的遗留构造,在 2.0 中成为遗留构造。请改用 set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)。 (有关 SQLAlchemy 2.0 的背景,请参阅:SQLAlchemy 2.0 - Major Migration Guide)

method as_scalar() → ScalarSelect[Any]

返回由此 Query 表示的完整 SELECT 语句,转换为标量子查询。

自版本 1.4 弃用:Query.as_scalar() 方法已弃用,并将在将来的版本中删除。请参考 Query.scalar_subquery()

method autoflush(setting: bool) → Self

返回具有特定“autoflush”设置的查询。

自 SQLAlchemy 1.4 起,Query.autoflush() 方法等效于在 ORM 级别使用 autoflush 执行选项。有关此选项的更多背景,请参阅 Autoflush 部分。

attribute column_descriptions

返回有关此 Query 将返回的列的元数据。

格式是一个字典列表:

user_alias = aliased(User, name='user2')
q = sess.query(User, User.id, user_alias)

# this expression:
q.column_descriptions

# would return:
[
 {
 'name':'User',
 'type':User,
 'aliased':False,
 'expr':User,
 'entity': User
 },
 {
 'name':'id',
 'type':Integer(),
 'aliased':False,
 'expr':User.id,
 'entity': User
 },
 {
 'name':'user2',
 'type':User,
 'aliased':True,
 'expr':user_alias,
 'entity': user_alias
 }
]

另请参阅

此 API 也可使用 2.0 风格 查询,文档位于:

  • 检查来自启用 ORM 的 SELECT 和 DML 语句的实体和列

  • Select.column_descriptions

method correlate(*fromclauses: Literal[None, False] | FromClauseRole | Type[Any] | Inspectable[_HasClauseElement[Any]] | _HasClauseElement[Any]) → Self

返回一个 Query 构造,将给定的 FROM 子句与封闭的 Queryselect() 关联起来。

此处的方法接受映射类、aliased() 构造和 Mapper 构造作为参数,这些参数会被解析为表达式构造,以及适当的表达式构造。

最终,相关参数将被强制转换为表达式构造,然后传递给 Select.correlate()

在这种情况下,相关参数会生效,例如在使用 Query.from_self() 时,或者在将由Query.subquery()返回的子查询嵌入到另一个select() 构造中时。

另请参阅

Select.correlate() - v2 等效方法。

method count() → int

返回此Query形成的 SQL 将返回的行数计数。

这将生成以下查询的 SQL 语句:

SELECT count(1) AS count_1 FROM (
 SELECT <rest of query follows...>
) AS anon_1

上述 SQL 返回一个单行,即 count 函数的聚合值;然后Query.count() 方法返回该单个整数值。

警告

需要注意的是,count() 返回的值并不等同于此 Query 通过 .all() 等方法返回的 ORM 对象数。当 Query 对象被要求返回完整实体时,将根据主键对条目进行重复消除,这意味着如果相同的主键值在结果中出现超过一次,则只会存在一个该主键的对象。这不适用于针对单个列的查询。

另请参阅

我的查询的返回对象数与 query.count() 告诉我的不一样 - 为什么?

对于对特定列进行精细控制的计数,跳过子查询的使用或以其他方式控制 FROM 子句,或使用其他聚合函数,可以结合使用expression.func表达式和 Session.query(),例如:

from sqlalchemy import func

# count User records, without
# using a subquery.
session.query(func.count(User.id))

# return count of user "id" grouped
# by "name"
session.query(func.count(User.id)).\
 group_by(User.name)

from sqlalchemy import distinct

# count distinct "name" values
session.query(func.count(distinct(User.name)))

另请参阅

2.0 迁移 - ORM 用法

method cte(name: str | None = None, recursive: bool = False, nesting: bool = False) → CTE

返回由此Query表示的完整 SELECT 语句,表示为公共表达式(CTE)。

参数和用法与 SelectBase.cte() 方法相同;有关更多详细信息,请参阅该方法。

这里是 PostgreSQL WITH RECURSIVE 示例。请注意,在此示例中,included_parts cte 和其 incl_alias 别名是核心可选择的,这意味着可以通过 .c. 属性访问列。parts_alias 对象是 Part 实体的 aliased() 实例,因此可以直接访问列映射属性:

from sqlalchemy.orm import aliased

class Part(Base):
 __tablename__ = 'part'
 part = Column(String, primary_key=True)
 sub_part = Column(String, primary_key=True)
 quantity = Column(Integer)

included_parts = session.query(
 Part.sub_part,
 Part.part,
 Part.quantity).\
 filter(Part.part=="our part").\
 cte(name="included_parts", recursive=True)

incl_alias = aliased(included_parts, name="pr")
parts_alias = aliased(Part, name="p")
included_parts = included_parts.union_all(
 session.query(
 parts_alias.sub_part,
 parts_alias.part,
 parts_alias.quantity).\
 filter(parts_alias.part==incl_alias.c.sub_part)
 )

q = session.query(
 included_parts.c.sub_part,
 func.sum(included_parts.c.quantity).
 label('total_quantity')
 ).\
 group_by(included_parts.c.sub_part)

请参阅

Select.cte() - v2 等效方法。

method delete(synchronize_session: SynchronizeSessionArgument = 'auto') → int

使用任意 WHERE 子句执行 DELETE。

从数据库中删除与此查询匹配的行。

例如:

sess.query(User).filter(User.age == 25).\
 delete(synchronize_session=False)

sess.query(User).filter(User.age == 25).\
 delete(synchronize_session='evaluate')

警告

请参阅 ORM-Enabled INSERT、UPDATE 和 DELETE 语句 章节以了解重要的注意事项和警告,包括在使用映射器继承配置时批量 UPDATE 和 DELETE 的限制。

参数:

synchronize_session – 选择在会话中更新对象属性的策略。请参阅 ORM-Enabled INSERT、UPDATE 和 DELETE 语句 章节讨论这些策略。

返回:

数据库的“行计数”功能返回的匹配行数。

请参阅

ORM-Enabled INSERT、UPDATE 和 DELETE 语句

method distinct(*expr: _ColumnExpressionArgument[Any]) → Self

对查询应用 DISTINCT 并返回新生成的 Query

注意

ORM 级别的 distinct() 调用包含逻辑,将自动将查询的 ORDER BY 中的列添加到 SELECT 语句的列子句中,以满足数据库后端的常见需求,即在使用 DISTINCT 时,ORDER BY 列应作为 SELECT 列的一部分。然而,这些列 不会 添加到实际由 Query 获取的列列表中,因此不会影响结果。然而,在使用 Query.statement 访问器时,这些列会通过。

自版本 2.0 起已弃用:此逻辑已弃用,将在 SQLAlchemy 2.0 中删除。请参阅 使用 DISTINCT 与其他列,但仅选择实体 了解 2.0 中此用例的描述。

请参阅

Select.distinct() - v2 等效方法。

参数:

*expr

可选的列表达式。当存在时,PostgreSQL 方言将呈现 DISTINCT ON (<expressions>) 结构。

自 1.4 版本起已弃用:在其他方言中使用*expr 已弃用,并将在将来的版本中引发CompileError

method enable_assertions(value: bool) → Self

控制是否生成断言。

当设置为 False 时,返回的 Query 在某些操作之前不会断言其状态,包括调用 filter() 时未应用 LIMIT/OFFSET,调用 get() 时不存在条件,以及调用 filter()/order_by()/group_by() 等时不存在“from_statement()”。此更宽松的模式由自定义的 Query 子类使用,以指定标准或其他修改器在通常的使用模式之外。

应注意确保使用模式是可行的。例如,由 from_statement()应用的语句将覆盖由 filter()或 order_by()设置的任何条件。

method enable_eagerloads(value: bool) → Self

控制是否呈现急切连接和子查询。

当设置为 False 时,返回的 Query 将不会渲染急切连接,无论 joinedload()subqueryload() 选项或映射器级别的 lazy='joined'/lazy='subquery' 配置如何。

当将 Query 的语句嵌套到子查询或其他可选择项中时,或者当使用Query.yield_per()时主要用于。

method except_(*q: Query) → Self

生成此 Query 对一项或多项查询的 EXCEPT。

Query.union()的工作方式相同。请参阅该方法以获取用法示例。

另请参阅

Select.except_() - v2 等效方法。

method except_all(*q: Query) → Self

生成此 Query 对一项或多项查询的 EXCEPT ALL。

Query.union()的工作方式相同。请参阅该方法以获取用法示例。

另请参阅

Select.except_all() - v2 等效方法。

method execution_options(**kwargs: Any) → Self

设置在执行期间生效的非 SQL 选项。

此处允许的选项包括所有被Connection.execution_options()接受的选项,以及一系列 ORM 特定选项:

populate_existing=True - 等效于使用Query.populate_existing()

autoflush=True|False - 等效于使用Query.autoflush()

yield_per=<value> - 等效于使用Query.yield_per()

注意,如果使用了Query.yield_per()方法或执行选项,则stream_results执行选项会自动启用。

版本 1.4 中的新功能:- 添加了 ORM 选项到Query.execution_options()

在使用 2.0 风格查询时,执行选项也可以在每次执行时指定,通过Session.execution_options参数。

警告

Connection.execution_options.stream_results参数不应在单个 ORM 语句执行的级别使用,因为Session不会跟踪来自不同模式转换映射的对象在单个会话中。对于单个Session范围内的多个模式转换映射,请参见水平分片。

另请参阅

使用服务器端游标(又名流式结果)

Query.get_execution_options()

Select.execution_options() - v2 等效方法。

method exists() → Exists

一个方便的方法,将查询转换为形式为 EXISTS(SELECT 1 FROM … WHERE …)的 EXISTS 子查询。

例如:

q = session.query(User).filter(User.name == 'fred')
session.query(q.exists())

生成类似于:

SELECT EXISTS (
 SELECT 1 FROM users WHERE users.name = :name_1
) AS anon_1

EXISTS 构造通常用于 WHERE 子句中:

session.query(User.id).filter(q.exists()).scalar()

请注意,某些数据库(如 SQL Server)不允许在 SELECT 的列子句中存在 EXISTS 表达式。要基于存在性选择简单的布尔值作为 WHERE,使用literal()

from sqlalchemy import literal

session.query(literal(True)).filter(q.exists()).scalar()

另请参阅

Select.exists() - v2 可比较的方法。

method filter(*criterion: _ColumnExpressionArgument[bool]) → Self

将给定的过滤条件应用于此Query的副本,使用 SQL 表达式。

例如:

session.query(MyClass).filter(MyClass.name == 'some name')

多个条件可以以逗号分隔的方式指定;效果是它们将使用and_()函数连接在一起:

session.query(MyClass).\
 filter(MyClass.name == 'some name', MyClass.id > 5)

条件是适用于 select 的 WHERE 子句的任何 SQL 表达式对象。字符串表达式通过text()构造被强制转换为 SQL 表达式构造。

另请参阅

Query.filter_by() - 根据关键字表达式进行过滤。

Select.where() - v2 等效方法。

method filter_by(**kwargs: Any) → Self

将给定的过滤条件应用于此Query的副本,使用关键字表达式。

例如:

session.query(MyClass).filter_by(name = 'some name')

可以指定多个条件,以逗号分隔;其效果是它们将使用and_()函数连接在一起:

session.query(MyClass).\
 filter_by(name = 'some name', id = 5)

关键字表达式是从查询的主要实体或最后一个曾被调用过Query.join()的目标实体中提取的。

另请参阅

Query.filter() - 根据 SQL 表达式进行过滤。

Select.filter_by() - v2 可比较的方法。

method first() → _T | None

返回此Query的第一个结果,如果结果不包含任何行,则返回 None。

first()在生成的 SQL 中应用了一个限制为 1,因此仅在服务器端生成一个主要实体行(请注意,如果存在联接加载的集合,则可能由多个结果行组成)。

调用Query.first()会导致基础查询的执行。

另请参阅

Query.one()

Query.one_or_none()

Result.first() - v2 可比较的方法。

Result.scalars() - v2 可比较的方法。

method from_statement(statement: ExecutableReturnsRows) → Self

执行给定的 SELECT 语句并返回结果。

此方法绕过所有内部语句编译,并且语句在不修改的情况下执行。

该语句通常是一个text()select()结构,应返回与此Query所代表的实体类相对应的列集。

另请参阅

Select.from_statement() - v2 可比较的方法。

method get(ident: _PKIdentityArgument) → Any | None

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

自版本 2.0 起已弃用:Query.get() 方法被认为是 SQLAlchemy 1.x 系列的遗留部分,并且在 2.0 中成为遗留构造。该方法现在可用作 Session.get()(关于 SQLAlchemy 2.0 的背景信息,请参阅:SQLAlchemy 2.0 - 主要迁移指南)

例如:

my_user = session.query(User).get(5)

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

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

Query.get() 特殊之处在于它提供对所属 Session 的标识映射的直接访问。如果给定的主键标识符存在于本地标识映射中,则对象将直接从此集合返回,而不会发出任何 SQL,除非对象已被标记为完全过期。如果不存在,则执行 SELECT 来定位对象。

Query.get() 会检查对象是否存在于标识映射中并标记为过期 - 会发出一个 SELECT 来刷新对象并确保行仍然存在。如果不存在,则会引发 ObjectDeletedError

Query.get() 仅用于返回单个映射实例,而不是多个实例或单个列构造,并且严格限于单个主键值。源 Query 必须以这种方式构造,即针对单个映射实体,没有额外的过滤条件。可以通过 Query.options() 应用加载选项,如果对象尚未在本地存在,则将使用该选项。

参数:

ident

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

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

my_object = query.get(5)

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

my_object = query.get((5, 10))

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

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

新版本 1.3 中的 Query.get() 方法现在可选择性地接受属性名到值的字典,以指示主键标识符。

返回:

对象实例,或 None

method get_children(*, omit_attrs: Tuple[str, ...] = (), **kw: Any) → Iterable[HasTraverseInternals]

继承自 HasTraverseInternals.get_children() 方法的 HasTraverseInternals

返回此 HasTraverseInternals 的即时子 HasTraverseInternals 元素。

这用于访问遍历。

**kw 可以包含改变返回集合的标志,例如为了减少更大的遍历而返回子集合中的项目,或者从不同的上下文中返回子项(例如模式级别的集合而不是从子句级别返回)。

method get_execution_options() → _ImmutableExecuteOptions

获取在执行期间生效的非 SQL 选项。

新版本 1.3 中新增。

另请参阅

Query.execution_options()

Select.get_execution_options() - v2 可比较方法。

attribute get_label_style

检索当前的标签样式。

新版本 1.4 中新增。

另请参阅

Select.get_label_style() - v2 等效方法。

method group_by(_Query__first: Literal[None, False, _NoArg.NO_ARG] | _ColumnExpressionOrStrLabelArgument[Any] = _NoArg.NO_ARG, *clauses: _ColumnExpressionOrStrLabelArgument[Any]) → Self

将一个或多个 GROUP BY 准则应用于查询,并返回新生成的 Query

所有现有的 GROUP BY 设置都可以通过传递 None 来抑制 - 这将抑制任何配置在映射器上的 GROUP BY。

另请参阅

这些部分描述了 GROUP BY,是以 2.0 样式 调用的,但也适用于 Query

带有 GROUP BY / HAVING 的聚合函数 - 在 SQLAlchemy 统一教程 中

按标签排序或分组 - 在 SQLAlchemy 统一教程 中

Select.group_by() - v2 等效方法。

method having(*having: _ColumnExpressionArgument[bool]) → Self

将 HAVING 准则应用于查询,并返回新生成的 Query

Query.having()Query.group_by() 结合使用。

HAVING 条件使得可以在聚合函数(如 COUNT、SUM、AVG、MAX 和 MIN)上使用过滤器,例如:

q = session.query(User.id).\
 join(User.addresses).\
 group_by(User.id).\
 having(func.count(Address.id) > 2)

另请参阅

Select.having() - v2 等效方法。

method instances(result_proxy: CursorResult[Any], context: QueryContext | None = None) → Any

针对CursorResultQueryContext返回一个 ORM 结果。

自 2.0 版本起已弃用:Query.instances()方法已弃用,并将在将来的版本中移除。请改为使用 Select.from_statement()方法或与 Session.execute()结合使用 aliased()构造。

method intersect(*q: Query) → Self

对此查询与一个或多个查询进行 INTERSECT。

Query.union()的工作方式相同。参见该方法的使用示例。

另请参阅

Select.intersect() - v2 等效方法。

method intersect_all(*q: Query) → Self

对此查询与一个或多个查询进行 INTERSECT ALL。

Query.union()的工作方式相同。参见该方法的使用示例。

另请参阅

Select.intersect_all() - v2 等效方法。

attribute is_single_entity

指示此Query是否返回元组或单个实体。

如果此查询对其结果列表中的每个实例返回单个实体,则返回 True,如果此查询对其结果返回实体的元组,则返回 False。

从版本 1.3.11 开始的新功能。

另请参阅

Query.only_return_tuples()

method join(target: _JoinTargetArgument, onclause: _OnClauseArgument | None = None, *, isouter: bool = False, full: bool = False) → Self

创建针对此Query对象的标准的 SQL JOIN,并应用生成性地返回新生成的Query

简单关系连接

考虑两个类UserAddress之间的映射,其中存在一个关系User.addresses表示与每个User关联的Address对象的集合。Query.join()的最常见用法是沿着这个关系创建一个 JOIN,使用User.addresses属性作为指示器指示应该如何发生:

q = session.query(User).join(User.addresses)

在上面的情况下,调用Query.join()沿着User.addresses将导致大致等同于以下 SQL 的结果:

SELECT user.id, user.name
FROM user JOIN address ON user.id = address.user_id

在上述示例中,我们将User.addresses称为传递给Query.join()的“on clause”,即,它指示如何构造 JOIN 的“ON”部分。

要构建连接的链,可以使用多个Query.join()调用。关联绑定属性一次暗示了连接的左侧和右侧:

q = session.query(User).\
 join(User.orders).\
 join(Order.items).\
 join(Item.keywords)

注意

如上例所示,调用 join()方法的顺序很重要。例如,如果我们在连接链中依次指定UserItemOrder,则 Query 将不知道如何正确连接;在这种情况下,根据传递的参数,它可能会引发一个不知道如何连接的错误,或者可能会产生无效的 SQL,数据库会因此而引发错误。在正确的实践中,应以使 JOIN 子句在 SQL 中呈现的方式调用Query.join()方法,并且每个调用应表示与之前内容的清晰链接。

连接到目标实体或可选择项

第二种形式的Query.join()允许将任何映射实体或核心可选择构造作为目标。在此用法中,Query.join()将尝试沿着两个实体之间的自然外键关系创建一个 JOIN:

q = session.query(User).join(Address)

在上述调用形式中,Query.join()会自动为我们创建“on 子句”。如果两个实体之间没有外键,或者如果目标实体与已在左侧的实体之间存在多个外键链接,从而创建连接需要更多信息,则此调用形式最终会引发错误。请注意,当指示连接到一个没有 ON 子句的目标时,不会考虑 ORM 配置的关系。

连接到具有 ON 子句的目标

第三种调用形式允许显式传递目标实体以及 ON 子句。一个包含 SQL 表达式作为 ON 子句的示例如下:

q = session.query(User).join(Address, User.id==Address.user_id)

上述形式也可以使用一个关联绑定属性作为 ON 子句:

q = session.query(User).join(Address, User.addresses)

上述语法对于希望连接到特定目标实体的别名的情况很有用。如果我们想要两次连接到Address,可以使用aliased()函数设置两个别名:

a1 = aliased(Address)
a2 = aliased(Address)

q = session.query(User).\
 join(a1, User.addresses).\
 join(a2, User.addresses).\
 filter(a1.email_address=='ed@foo.com').\
 filter(a2.email_address=='ed@bar.com')

使用关联绑定调用形式还可以使用PropComparator.of_type()方法指定目标实体;与上面的查询等效的查询如下:

a1 = aliased(Address)
a2 = aliased(Address)

q = session.query(User).\
 join(User.addresses.of_type(a1)).\
 join(User.addresses.of_type(a2)).\
 filter(a1.email_address == 'ed@foo.com').\
 filter(a2.email_address == 'ed@bar.com')

增强内置 ON 子句

作为为现有关系提供完整自定义 ON 条件的替代方法,可以将PropComparator.and_()函数应用于关系属性,以将额外条件增加到 ON 子句中;附加条件将使用 AND 与默认条件组合:

q = session.query(User).join(
 User.addresses.and_(Address.email_address != 'foo@bar.com')
)

版本 1.4 中的新功能。

连接到表和子查询

加入的目标也可以是任何表或 SELECT 语句,它可能与目标实体相关或不相关。使用适当的.subquery()方法以将查询转换为子查询:

subq = session.query(Address).\
 filter(Address.email_address == 'ed@foo.com').\
 subquery()

q = session.query(User).join(
 subq, User.id == subq.c.user_id
)

通过使用aliased()将子查询链接到实体,可以以特定关系和/或目标实体的术语连接到子查询:

subq = session.query(Address).\
 filter(Address.email_address == 'ed@foo.com').\
 subquery()

address_subq = aliased(Address, subq)

q = session.query(User).join(
 User.addresses.of_type(address_subq)
)

控制从何处连接

在当前Query状态的左侧与我们要连接的内容不一致的情况下,可以使用Query.select_from()方法:

q = session.query(Address).select_from(User).\
 join(User.addresses).\
 filter(User.name == 'ed')

这将生成类似于以下 SQL:

SELECT address.* FROM user
 JOIN address ON user.id=address.user_id
 WHERE user.name = :name_1

另请参阅

Select.join() - v2 相当的方法。

参数:

  • *props – 用于Query.join()的传入参数,现代用法中的 props 集合应视为一种或两种参数形式,即作为单个“目标”实体或 ORM 属性绑定关系,或作为目标实体加上一个“on clause”,该“on clause”可以是 SQL 表达式或 ORM 属性绑定关系。

  • isouter=False – 如果为 True,则使用的连接将是左外连接,就像调用了Query.outerjoin()方法一样。

  • full=False – 渲染 FULL OUTER JOIN;隐含isouter

method label(name: str | None) → Label[Any]

返回由此Query表示的完整 SELECT 语句,转换为具有给定名称标签的标量子查询。

另请参阅

Select.label() - v2 类似的方法。

attribute lazy_loaded_from

正在将此Query用于惰性加载操作的InstanceState

从版本 1.4 开始不推荐使用:此属性应通过ORMExecuteState.lazy_loaded_from属性查看,在SessionEvents.do_orm_execute()事件的上下文中。

另请参阅

ORMExecuteState.lazy_loaded_from

method limit(limit: _LimitOffsetType) → Self

对查询应用 LIMIT 并返回新生成的 Query

另请参阅

Select.limit() - v2 等效方法。

method merge_result(iterator: FrozenResult[Any] | Iterable[Sequence[Any]] | Iterable[object], load: bool = True) → FrozenResult[Any] | Iterable[Any]

将结果合并到此 Query 对象的会话中。

自版本 2.0 弃用:Query.merge_result() 方法被视为 SQLAlchemy 1.x 系列的遗留构造,并在 2.0 中成为遗留构造。该方法已被 merge_frozen_result() 函数取代。 (有关 SQLAlchemy 2.0 的背景信息,请参阅:SQLAlchemy 2.0 - 主要迁移指南)

给定与此查询相同结构的 Query 返回的迭代器,返回一个相同的结果迭代器,所有映射实例都使用 Session.merge() 合并到会话中。 这是一种优化方法,将合并所有映射实例,保留结果行的结构和未映射列,比显式为每个值调用 Session.merge() 的方法开销小。

结果的结构是基于此 Query 的列列表确定的 - 如果这些列不对应,将会发生未经检查的错误。

‘load’ 参数与 Session.merge() 相同。

有关 Query.merge_result() 的用法示例,请参阅示例 Dogpile Caching 的源代码,其中 Query.merge_result() 用于有效地从缓存中恢复状态到目标 Session

method offset(offset: _LimitOffsetType) → Self

对查询应用 OFFSET ��返回新生成的 Query

另请参阅

Select.offset() - v2 等效方法。

method one() → _T

返回确切的一个结果或引发异常。

如果查询未选择任何行,则引发 sqlalchemy.orm.exc.NoResultFound。如果返回多个对象标识,或者对于仅返回标量值而不是完全映射实体的查询返回多行,则引发 sqlalchemy.orm.exc.MultipleResultsFound

调用one()会导致执行底层查询。

另请参见

Query.first()

Query.one_or_none()

Result.one() - v2 可比较方法。

Result.scalar_one() - v2 可比较方法。

method one_or_none() → _T | None

返回最多一个结果或引发异常。

如果查询未选择任何行,则返回None。 如果返回多个对象标识,或者如果对于返回标量值而不是完整标识映射的实体的查询返回多行,则引发sqlalchemy.orm.exc.MultipleResultsFound

调用Query.one_or_none()会导致执行底层查询。

另请参见

Query.first()

Query.one()

Result.one_or_none() - v2 可比较方法。

Result.scalar_one_or_none() - v2 可比较方法。

method only_return_tuples(value: bool) → Query

当设置为 True 时,查询结果将始终是一个Row对象。

这可以将通常返回单个实体作为标量的查询,在所有情况下返回一个Row结果。

另请参见

Query.tuples() - 返回元组,但在类型级别上也将结果类型化为Tuple

Query.is_single_entity()

Result.tuples() - v2 可比较方法。

method options(*args: ExecutableOption) → Self

返回一个新的Query对象,应用给定的映射器选项列表。

大多数提供的选项都涉及更改如何加载列和关系映射的属性。

另请参见

列加载选项

使用加载选项进行关系加载

method order_by(_Query__first: Literal[None, False, _NoArg.NO_ARG] | _ColumnExpressionOrStrLabelArgument[Any] = _NoArg.NO_ARG, *clauses: _ColumnExpressionOrStrLabelArgument[Any]) → Self

应用一个或多个 ORDER BY 标准到查询,并返回新生成的Query

例如:

q = session.query(Entity).order_by(Entity.id, Entity.name)

多次调用此方法等效于一次将所有子句连接起来调用。所有现有的 ORDER BY 条件都可以通过单独传递None来取消。然后可以通过再次调用Query.order_by()来添加新的 ORDER BY 条件,例如:

# will erase all ORDER BY and ORDER BY new_col alone
q = q.order_by(None).order_by(new_col)

另请参阅

这些部分描述了按 2.0 风格调用的 ORDER BY,但也适用于Query

ORDER BY - 在 SQLAlchemy 统一教程中

按标签排序或分组 - 在 SQLAlchemy 统一教程中

Select.order_by() - v2 等效方法。

method outerjoin(target: _JoinTargetArgument, onclause: _OnClauseArgument | None = None, *, full: bool = False) → Self

在此Query对象的条件上创建左外连接,并在生成式上应用,返回新生成的Query

使用方法与join()方法相同。

另请参阅

Select.outerjoin() - v2 等效方法。

method params(_Query__params: Dict[str, Any] | None = None, **kw: Any) → Self

为可能已在 filter() 中指定的绑定参数添加值。

参数可以使用kwargs 指定,或者作为第一个位置参数使用单个字典。两者之所以都存在是因为kwargs 很方便,但是一些参数字典包含 Unicode 键,**kwargs 就不能用。

method populate_existing() → Self

返回一个将在加载时过期并刷新所有实例,或者从当前Session中重用的Query

从 SQLAlchemy 1.4 开始,Query.populate_existing()方法等效于在 ORM 级别使用populate_existing执行选项。有关此选项的更多背景信息,请参见 填充现有 部分。

method prefix_with(*prefixes: _TextCoercedExpressionArgument[Any], dialect: str = '*') → Self

继承自 HasPrefixes.prefix_with() 方法的 HasPrefixes

在语句关键字后添加一个或多个表达式,即 SELECT、INSERT、UPDATE 或 DELETE。生成式。

这用于支持特定于后端的前缀关键字,例如 MySQL 提供的关键字。

例如:

stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql")

# MySQL 5.7 optimizer hints
stmt = select(table).prefix_with(
 "/*+ BKA(t1) */", dialect="mysql")

可以通过多次调用HasPrefixes.prefix_with()来指定多个前缀。

参数:

  • *prefixes – 文本或ClauseElement 构造,将在插入、更新或删除关键字之后呈现。

  • dialect – 可选的字符串方言名称,将仅限于将此前缀呈现为该方言。

method reset_joinpoint() → Self

返回一个新的 Query,其中“连接点”已被重置回查询的基本 FROM 实体。

该方法通常与 Query.join() 方法的 aliased=True 特性一起使用。请参阅 Query.join() 中的示例,了解其使用方法。

method scalar() → Any

返回第一个结果的第一个元素,如果没有行存在则返回 None。如果返回多行,则引发 MultipleResultsFound。

>>> session.query(Item).scalar()
<Item>
>>> session.query(Item.id).scalar()
1
>>> session.query(Item.id).filter(Item.id < 0).scalar()
None
>>> session.query(Item.id, Item.name).scalar()
1
>>> session.query(func.count(Parent.id)).scalar()
20

这将导致执行基础查询。

另请参阅

Result.scalar() - v2 可比较方法。

method scalar_subquery() → ScalarSelect[Any]

返回由此 Query 表示的完整 SELECT 语句,转换为标量子查询。

类似于 SelectBase.scalar_subquery()

自版本 1.4 起变更:Query.scalar_subquery() 方法取代了 Query.as_scalar() 方法。

另请参阅

Select.scalar_subquery() - v2 可比较方法。

method select_from(*from_obj: FromClauseRole | Type[Any] | Inspectable[_HasClauseElement[Any]] | _HasClauseElement[Any]) → Self

显式设置此 Query 的 FROM 子句。

Query.select_from() 常常与 Query.join() 结合使用,以控制从连接的“左”侧选择的实体。

此处的实体或可选择对象有效地替换了任何对 Query.join() 的调用的“左边缘”,当没有其他方式建立连接点时 - 通常,默认的“连接点”是查询对象的要选择的实体列表中最左边的实体。

一个典型的例子:

q = session.query(Address).select_from(User).\
 join(User.addresses).\
 filter(User.name == 'ed')

这将生成等效于以下 SQL:

SELECT address.* FROM user
JOIN address ON user.id=address.user_id
WHERE user.name = :name_1

参数:

*from_obj – 一个或多个要应用于 FROM 子句的实体集合。实体可以是映射类、AliasedClass对象、Mapper对象,以及核心FromClause元素,如子查询。

另请参阅

Query.join()

Query.select_entity_from()

Select.select_from() - v2 等效方法。

attribute selectable

返回由此Query发出的Select对象。

用于inspect()兼容性,这相当于:

query.enable_eagerloads(False).with_labels().statement
method set_label_style(style: SelectLabelStyle) → Self

将列标签应用于 Query.statement 的返回值。

表示此查询的语句访问器应返回一个 SELECT 语句,该语句将标签应用于形式为_的所有列;这通常用于消除具有相同名称的多个表中的列的歧义。

当查询实际发出 SQL 以加载行时,它总是使用列标签。

注意

Query.set_label_style()方法应用于Query.statement的输出,应用于Query本身的任何结果行调用系统,例如Query.first()Query.all()等。要使用Query.set_label_style()执行查询,请使用Session.execute()调用Query.statement

result = session.execute(
 query
 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
 .statement
)

1.4 版本中的新功能。

另请参阅

Select.set_label_style() - v2 等效方法。

method slice(start: int, stop: int) → Self

计算由给定索引表示的Query的“切片”,并返回结果Query

开始和停止索引的行为类似于 Python 内置range()函数的参数。此方法提供了使用LIMIT/OFFSET来获取查询的切片的替代方法。

例如,

session.query(User).order_by(User.id).slice(1, 3)

渲染为

SELECT  users.id  AS  users_id,
  users.name  AS  users_name
FROM  users  ORDER  BY  users.id
LIMIT  ?  OFFSET  ?
(2,  1)

另请参阅

Query.limit()

Query.offset()

Select.slice() - v2 等效方法。

attribute statement

由此 Query 表示的完整 SELECT 语句。

该语句默认情况下不会对构造应用歧义标签,除非首先调用 with_labels(True)。

method subquery(name: str | None = None, with_labels: bool = False, reduce_columns: bool = False) → Subquery

返回由此 Query 表示的完整 SELECT 语句,嵌入在一个 Alias 中。

查询中禁用了急切的 JOIN 生成。

另请参阅

Select.subquery() - v2 可比较方法。

参数:

  • name – 要分配为别名的字符串名称;这将传递给 FromClause.alias()。如果为 None,则在编译时将确定性地生成一个名称。

  • with_labels – 如果为 True,则首先将 with_labels() 应用于 Query,以将表限定标签应用于所有列。

  • reduce_columns – 如果为 True,则将调用 Select.reduce_columns() 来删除结果 select() 构造中的同名列,其中一个还通过外键或 WHERE 子句等价关系引用另一个。

method suffix_with(*suffixes: _TextCoercedExpressionArgument[Any], dialect: str = '*') → Self

继承自 HasSuffixes.suffix_with() 方法 HasSuffixes

将作为整个语句后的一个或多个表达式添加。

这用于支持特定于后端的后缀关键字在某些构造上。

例如:

stmt = select(col1, col2).cte().suffix_with(
 "cycle empno set y_cycle to 1 default 0", dialect="oracle")

可以通过多次调用 HasSuffixes.suffix_with() 来指定多个后缀。

参数:

  • *suffixes – 将在目标子句后呈现的文本或 ClauseElement 构造。

  • dialect – 可选的字符串方言名称,将限制仅将此后缀呈现为该方言。

method tuples() → Query

返回这个Query的元组类型形式。

此方法调用Query.only_return_tuples()方法,并将其值设置为True,这本身就确保了这个Query总是返回Row对象,即使查询是针对单个实体的。然后,它还会在类型级别返回一个“类型化”的查询,如果可能的话,该查询将将结果行类型化为具有类型的元组对象。

这种方法可以与Result.tuples()方法进行比较,该方法返回“self”,但从类型的角度来看,返回一个将产生带有类型的Tuple对象的对象。只有当这个Query对象已经是一个类型化的查询对象时,类型才会生效。

版本 2.0 中的新功能。

另请参阅

Result.tuples() - v2 等效方法。

method union(*q: Query) → Self

对一个或多个查询执行 UNION。

例如:

q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')

q3 = q1.union(q2)

该方法接受多个查询对象,以控制嵌套的级别。一系列union()调用,如下所示:

x.union(y).union(z).all()

将在每个union()上进行嵌套,并生成:

SELECT * FROM (SELECT * FROM (SELECT * FROM X UNION
 SELECT * FROM y) UNION SELECT * FROM Z)

而:

x.union(y, z).all()

生成:

SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION
 SELECT * FROM Z)

请注意,许多数据库后端不允许在 UNION、EXCEPT 等内部调用的查询上渲染 ORDER BY。要禁用所有 ORDER BY 子句,包括在映射器上配置的子句,请发出query.order_by(None) - 结果的Query对象将不会在其 SELECT 语句中渲染 ORDER BY。

另请参阅

Select.union() - v2 等效方法。

method union_all(*q: Query) → Self

对一个或多个查询执行 UNION ALL。

Query.union()的工作方式相同。请参阅该方法以获取用法示例。

另请参阅

Select.union_all() - v2 等效方法。

method update(values: Dict[_DMLColumnArgument, Any], synchronize_session: SynchronizeSessionArgument = 'auto', update_args: Dict[Any, Any] | None = None) → int

使用任意 WHERE 子句执行 UPDATE。

更新数据库中与此查询匹配的行。

例如:

sess.query(User).filter(User.age == 25).\
 update({User.age: User.age - 10}, synchronize_session=False)

sess.query(User).filter(User.age == 25).\
 update({"age": User.age - 10}, synchronize_session='evaluate')

警告

查看 ORM 启用的 INSERT、UPDATE 和 DELETE 语句一节,了解重要的警告和注意事项,包括在使用任意 UPDATE 和 DELETE 与映射器继承配置时的限制。

参数:

  • values – 一个包含属性名称的字典,或者作为键的映射属性或 SQL 表达式,以及作为值的文字值或 SQL 表达式。如果希望使用 参数排序模式,则值可以作为 2 元组的列表传递;这要求将 update.preserve_parameter_order 标志也传递给 Query.update.update_args 字典。

  • synchronize_session – 选择在会话中更新对象属性的策略。参见 ORM-Enabled INSERT, UPDATE, and DELETE statements 章节,讨论这些策略。

  • update_args – 可选字典,如果存在,则会作为对象的 update() 构造函数的 **kw 参数传递给底层。可以用于传递特定于方言的参数,如 mysql_limit,以及其他特殊参数,如 update.preserve_parameter_order

返回:

数据库的“行计数”功能返回的匹配行数。

另请参见

ORM-Enabled INSERT, UPDATE, and DELETE statements

method value(column: _ColumnExpressionArgument[Any]) → Any

返回与给定列表达式对应的标量结果。

自版本 1.4 起弃用:Query.value() 已弃用,并将在将来的版本中删除。请结合使用 Query.with_entities()Query.scalar()

method values(*columns: _ColumnsClauseArgument[Any]) → Iterable[Any]

返回一个迭代器,生成与给定列列表对应的结果元组。

自版本 1.4 起弃用:Query.values() 已弃用,并将在将来的版本中删除。请使用 Query.with_entities()

method where(*criterion: _ColumnExpressionArgument[bool]) → Self

Query.filter() 的别名。

版本 1.4 中的新功能。

另请参见

Select.where() - v2 等效方法。

attribute whereclause

返回此查询的当前 WHERE 条件的只读属性。

返回的值是一个 SQL 表达式构造,如果没有建立条件,则为 None

另请参见

Select.whereclause - v2 等效属性。

method with_entities(*entities: _ColumnsClauseArgument[Any], **_Query__kw: Any) → Query[Any]

返回一个用给定实体替换 SELECT 列表的新Query

例如:

# Users, filtered on some arbitrary criterion
# and then ordered by related email address
q = session.query(User).\
 join(User.address).\
 filter(User.name.like('%ed%')).\
 order_by(Address.email)

# given *only* User.id==5, Address.email, and 'q', what
# would the *next* User in the result be ?
subq = q.with_entities(Address.email).\
 order_by(None).\
 filter(User.id==5).\
 subquery()
q = q.join((subq, subq.c.email < Address.email)).\
 limit(1)

另请参阅

Select.with_only_columns() - v2 可比较方法。

method with_for_update(*, nowait: bool = False, read: bool = False, of: _ForUpdateOfArgument | None = None, skip_locked: bool = False, key_share: bool = False) → Self

返回一个具有指定FOR UPDATE子句选项的新Query

此方法的行为与GenerativeSelect.with_for_update()相同。当没有参数调用时,生成的 SELECT 语句将附加一个 FOR UPDATE 子句。当指定了额外的参数时,如 FOR UPDATE NOWAITLOCK IN SHARE MODE,特定于后端的选项会生效。

例如:

q = sess.query(User).populate_existing().with_for_update(nowait=True, of=User)

在 PostgreSQL 后端上执行上述查询会呈现如下:

SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT

警告

在使用with_for_update来进行急加载关系时,它并不受 SQLAlchemy 官方支持或推荐,并且可能无法与各种数据库后端上的某些查询一起正常工作。当成功使用with_for_update与涉及到joinedload()的查询时,SQLAlchemy 将尝试生成锁定所有涉及的表的 SQL。

注意

通常在使用Query.with_for_update()方法时,结合使用Query.populate_existing()方法是一个好主意。Query.populate_existing()的目的是强制将从 SELECT 中读取的所有数据都填充到返回的 ORM 对象中,即使这些对象已经存在于标识映射中。

另请参阅

GenerativeSelect.with_for_update() - 具有完整参数和行为描述的核心级方法。

Query.populate_existing() - 覆盖已加载到标识映射中的对象的属性。

method with_hint(selectable: _FromClauseArgument, text: str, dialect_name: str = '*') → Self

继承自 HasHints.with_hint() 方法的 HasHints

为给定的可选对象添加索引或其他执行上下文提示到这个Select或其他可选对象中。

提示的文本将根据正在使用的数据库后端在给定的 TableAlias 中的适当位置进行渲染。方言实现通常使用 Python 字符串替换语法,其中令牌 %(name)s 用于呈现表或别名的名称。例如,在使用 Oracle 时,以下内容:

select(mytable).\
 with_hint(mytable, "index(%(name)s ix_mytable)")

渲染 SQL 如下:

select /*+ index(mytable ix_mytable) */ ... from mytable

dialect_name 选项将限制特定后端的特定提示的渲染。例如,同时为 Oracle 和 Sybase 添加提示:

select(mytable).\
 with_hint(mytable, "index(%(name)s ix_mytable)", 'oracle').\
 with_hint(mytable, "WITH INDEX ix_mytable", 'mssql')

另请参阅

Select.with_statement_hint()

method with_labels() → Self

自 2.0 版本起弃用:Query.with_labels()Query.apply_labels() 方法在 SQLAlchemy 1.x 系列中被视为遗留,且在 2.0 版本中成为遗留构造。请改用 set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)。(关于 SQLAlchemy 2.0 的背景信息请参见:SQLAlchemy 2.0 - 主要迁移指南)

method with_parent(instance: object, property: attributes.QueryableAttribute[Any] | None = None, from_entity: _ExternalEntityType[Any] | None = None) → Self

添加筛选条件,将给定实例与子对象或集合关联起来,使用其属性状态以及已建立的 relationship() 配置。

自 2.0 版本起弃用:Query.with_parent() 方法在 SQLAlchemy 1.x 系列中被视为遗留,且在 2.0 版本中成为遗留构造。请使用独立构造的 with_parent()。(关于 SQLAlchemy 2.0 的背景信息请参见:SQLAlchemy 2.0 - 主要迁移指南)

此方法使用 with_parent() 函数生成子句,其结果传递给 Query.filter()

参数与 with_parent() 相同,唯一的例外是给定属性可以为 None,在这种情况下,将针对此 Query 对象的目标映射器执行搜索。

参数:

  • instance – 具有一些 relationship() 的实例。

  • property – 表示应使用实例哪个关系来协调父/子关系的类绑定属性。

  • from_entity – 要考虑为左侧的实体。默认为Query本身的“零”实体。

method with_session(session: Session) → Self

返回一个将使用给定SessionQuery

虽然Query对象通常是使用Session.query()方法实例化的,但也可以直接构建Query而无需必然使用Session。这样的Query对象,或者已与不同Session关联的任何Query对象,可以使用此方法生成一个与目标会话关联的新Query对象:

from sqlalchemy.orm import Query

query = Query([MyClass]).filter(MyClass.id == 5)

result = query.with_session(my_session).one()
method with_statement_hint(text: str, dialect_name: str = '*') → Self

继承自 HasHints.with_statement_hint() 方法的 HasHints

为此Select或其他可选择的对象添加语句提示。

此方法类似于Select.with_hint(),不过不需要单独的表,而是适用于整个语句。

这里的提示是特定于后端数据库的,并且可能包括隔离级别、文件指令、提取指令等指令。

另请参阅

Select.with_hint()

Select.prefix_with() - 通用的 SELECT 前缀,也可以适用于某些数据库特定的 HINT 语法,如 MySQL 优化器提示

method with_transformation(fn: Callable[[Query], Query]) → Query

通过给定的函数返回一个经过转换的新Query对象。

例如:

def filter_something(criterion):
 def transform(q):
 return q.filter(criterion)
 return transform

q = q.with_transformation(filter_something(x==5))

这允许为Query对象创建临时配方。

method yield_per(count: int) → Self

一次只产出count行。

此方法的目的是在获取非常大的结果集(> 10K 行)时,将结果批处理到子集合中并部分地将其产出,以便 Python 解释器不需要声明非常大的内存区域,这既费时又导致内存使用过多。当使用合适的产出设置(例如大约 1000)时,即使使用缓冲行的 DBAPI(大多数情况下都是),从获取数十万行的性能通常也会提高一倍。

从 SQLAlchemy 1.4 开始,Query.yield_per()方法等同于在 ORM 级别使用yield_per执行选项。有关此选项的更多背景信息,请参阅使用 Yield Per 获取大型结果集部分。

另请参阅

使用 Yield Per 获取大型结果集

ORM 特定查询构造

本节已移至附加 ORM API 构造。

查询对象

Query是根据给定的Session使用Session.query()方法生成的:

q = session.query(SomeMappedClass)

以下是Query对象的完整接口。

对象名称 描述
Query ORM 级别的 SQL 构造对象。
class sqlalchemy.orm.Query

ORM 级别的 SQL 构造对象。

遗留功能

ORM Query对象是 SQLAlchemy 2.0 的遗留构造。请参阅遗留查询 API 顶部的注释,包括迁移文档的链接。

Query 对象通常最初是使用Session.query()方法从Session生成的,并在较少的情况下通过直接实例化Query并使用Query.with_session()方法与Session关联。

成员

init(), add_column(), add_columns(), add_entity(), all(), apply_labels(), as_scalar(), autoflush(), column_descriptions, correlate(), count(), cte(), delete(), distinct(), enable_assertions(), enable_eagerloads(), except_(), except_all(), execution_options(), exists(), filter(), filter_by(), first(), from_statement(), get(), get_children(), get_execution_options(), get_label_style, group_by(), having(), instances(), intersect(), intersect_all(), is_single_entity, join(), label(), lazy_loaded_from, limit(), merge_result(), offset(), one(), one_or_none(), only_return_tuples(), options(), order_by(), outerjoin(), params(), populate_existing(), prefix_with(), reset_joinpoint(), scalar(), scalar_subquery(), select_from(), selectable, set_label_style(), slice(), statement, subquery(), suffix_with(), tuples(), union(), union_all(), update(), value(), values(), where(), whereclause, with_entities(), with_for_update(), with_hint(), with_labels(), with_parent(), with_session(), with_statement_hint(), with_transformation(), yield_per()

类签名

sqlalchemy.orm.Query (sqlalchemy.sql.expression._SelectFromElements, sqlalchemy.sql.annotation.SupportsCloneAnnotations, sqlalchemy.sql.expression.HasPrefixes, sqlalchemy.sql.expression.HasSuffixes, sqlalchemy.sql.expression.HasHints, sqlalchemy.event.registry.EventTarget, sqlalchemy.log.Identified, sqlalchemy.sql.expression.Generative, sqlalchemy.sql.expression.Executable, typing.Generic)

method __init__(entities: _ColumnsClauseArgument[Any] | Sequence[_ColumnsClauseArgument[Any]], session: Session | None = None)

直接构造一个 Query

例如:

q = Query([User, Address], session=some_session)

以上等同于:

q = some_session.query(User, Address)

参数:

  • entities – 一个实体和/或 SQL 表达式序列。

  • session – 一个 Session,将与 Query 关联。可选;也可以通过 Query.with_session() 方法将 QuerySession 关联。

另请参阅

Session.query()

Query.with_session()

method add_column(column: _ColumnExpressionArgument[Any]) → Query[Any]

将一个列表达式添加到要返回的结果列列表中。

自版本 1.4 起已弃用:Query.add_column() 已弃用,将在未来的发布中删除。请使用 Query.add_columns()

method add_columns(*column: _ColumnExpressionArgument[Any]) → Query[Any]

将一个或多个列表达式添加到要返回的结果列列表中。

另请参阅

Select.add_columns() - v2 可比较的方法。

method add_entity(entity: _EntityType[Any], alias: Alias | Subquery | None = None) → Query[Any]

将一个映射实体添加到要返回的结果列列表中。

另请参阅

Select.add_columns() - v2 可比较的方法。

method all() → List[_T]

将此 Query 表示的结果作为列表返回。

这将导致底层 SQL 语句的执行。

警告

当询问 Query 对象返回由全 ORM 映射的实体组成的序列或迭代器时,将根据主键对条目进行去重。有关更多详细信息,请参阅 FAQ。

另请参阅

我的查询返回的对象数与 query.count() 告诉我的不同 - 为什么?

另请参阅

Result.all() - v2 可比较的方法。

Result.scalars() - v2 可比较的方法。

method apply_labels() → Self

自 2.0 版以来已弃用:Query.with_labels()Query.apply_labels() 方法被视为 SQLAlchemy 1.x 系列的传统构造,并在 2.0 版中成为传统构造。请改用 set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)。 (有关 SQLAlchemy 2.0 的背景,请参阅:SQLAlchemy 2.0 - 主要迁移指南)

method as_scalar() → ScalarSelect[Any]

将由此 Query 表示的完整 SELECT 语句转换为标量子查询。

自 1.4 版以来已弃用:Query.as_scalar() 方法已弃用,并将在将来的版本中删除。请参阅 Query.scalar_subquery()

method autoflush(setting: bool) → Self

返回具有特定 'autoflush' 设置的查询。

自 SQLAlchemy 1.4 起,Query.autoflush() 方法等同于在 ORM 级别使用 autoflush 执行选项。有关此选项的更多背景,请参阅自动刷新部分。

attribute column_descriptions

返回由此 Query 返回的列的元数据。

格式是一个字典列表:

user_alias = aliased(User, name='user2')
q = sess.query(User, User.id, user_alias)

# this expression:
q.column_descriptions

# would return:
[
 {
 'name':'User',
 'type':User,
 'aliased':False,
 'expr':User,
 'entity': User
 },
 {
 'name':'id',
 'type':Integer(),
 'aliased':False,
 'expr':User.id,
 'entity': User
 },
 {
 'name':'user2',
 'type':User,
 'aliased':True,
 'expr':user_alias,
 'entity': user_alias
 }
]

另请参阅

此 API 也可以使用 2.0 风格 查询,在以下文档中有所记录:

  • 检查来自启用 ORM 的 SELECT 和 DML 语句的实体和列

  • Select.column_descriptions

method correlate(*fromclauses: Literal[None, False] | FromClauseRole | Type[Any] | Inspectable[_HasClauseElement[Any]] | _HasClauseElement[Any]) → Self

返回一个将给定的 FROM 子句与封闭的 Queryselect() 相关联的 Query 构造。

此处的方法接受映射类、aliased() 构造和 Mapper 构造作为参数,它们会被解析为表达式构造,以及适当的表达式构造。

相关参数最终在转换为表达式构造后传递给 Select.correlate()

在诸如使用 Query.from_self() 或者当由 Query.subquery() 返回的子查询嵌入到另一个 select() 构造中时,相关参数才会生效。

另请参阅

Select.correlate() - v2 等效方法。

method count() → int

返回此 Query 生成的 SQL 所返回的行数的计数。

这将生成此查询的 SQL 如下:

SELECT count(1) AS count_1 FROM (
 SELECT <rest of query follows...>
) AS anon_1

上述 SQL 返回单行,这是计数函数的聚合值;然后 Query.count() 方法返回该单个整数值。

警告

重要的是要注意,count() 返回的值 与此 Query 从 .all() 方法等返回的 ORM 对象数不同。当 Query 对象被要求返回完整实体时,将根据主键去重,这意味着如果相同的主键值在结果中出现多次,则只会存在一个该主键的对象。这不适用于针对单个列的查询。

另请参阅

我的查询返回的对象数量与 query.count() 告诉我的不同 - 为什么?

要对特定列进行精细控制以进行计数,跳过子查询的使用或以其他方式控制 FROM 子句,或者使用其他聚合函数,请结合 Session.query() 中的 expression.func 表达式,例如:

from sqlalchemy import func

# count User records, without
# using a subquery.
session.query(func.count(User.id))

# return count of user "id" grouped
# by "name"
session.query(func.count(User.id)).\
 group_by(User.name)

from sqlalchemy import distinct

# count distinct "name" values
session.query(func.count(distinct(User.name)))

另请参阅

2.0 迁移 - ORM 使用

method cte(name: str | None = None, recursive: bool = False, nesting: bool = False) → CTE

返回由此 Query 表示的完整 SELECT 语句,表示为一个通用表达式(CTE)。

参数和用法与SelectBase.cte()方法相同;请参阅该方法以获取更多详细信息。

这里是PostgreSQL WITH RECURSIVE 示例。请注意,在此示例中,included_parts cte 和其别名incl_alias都是 Core 可选择项,这意味着可以通过.c.属性访问列。parts_alias对象是Part实体的aliased()实例,因此可以直接访问映射到列的属性:

from sqlalchemy.orm import aliased

class Part(Base):
 __tablename__ = 'part'
 part = Column(String, primary_key=True)
 sub_part = Column(String, primary_key=True)
 quantity = Column(Integer)

included_parts = session.query(
 Part.sub_part,
 Part.part,
 Part.quantity).\
 filter(Part.part=="our part").\
 cte(name="included_parts", recursive=True)

incl_alias = aliased(included_parts, name="pr")
parts_alias = aliased(Part, name="p")
included_parts = included_parts.union_all(
 session.query(
 parts_alias.sub_part,
 parts_alias.part,
 parts_alias.quantity).\
 filter(parts_alias.part==incl_alias.c.sub_part)
 )

q = session.query(
 included_parts.c.sub_part,
 func.sum(included_parts.c.quantity).
 label('total_quantity')
 ).\
 group_by(included_parts.c.sub_part)

另请参阅

Select.cte() - v2 等效方法。

method delete(synchronize_session: SynchronizeSessionArgument = 'auto') → int

使用任意 WHERE 子句执行 DELETE。

从数据库中删除此查询匹配的行。

例如:

sess.query(User).filter(User.age == 25).\
 delete(synchronize_session=False)

sess.query(User).filter(User.age == 25).\
 delete(synchronize_session='evaluate')

警告

请参阅 ORM-Enabled INSERT、UPDATE 和 DELETE 语句以获取重要的注意事项和警告,包括在使用 mapper 继承配置时使用批量 UPDATE 和 DELETE 时的限制。

参数:

synchronize_session – 选择更新会话中对象属性的策略。请参阅 ORM-Enabled INSERT、UPDATE 和 DELETE 语句部分,了解这些策略的讨论。

返回:

由数据库的“行计数”功能返回的匹配行数。

另请参阅

ORM-Enabled INSERT、UPDATE 和 DELETE 语句

method distinct(*expr: _ColumnExpressionArgument[Any]) → Self

对查询应用DISTINCT并返回新结果的Query

注意

ORM 级别的distinct()调用包括逻辑,将查询的 ORDER BY 中的列自动添加到 SELECT 语句的列子句中,以满足数据库后端的常见需求,即使用 DISTINCT 时,ORDER BY 列必须是 SELECT 列的一部分。然而,这些列不会添加到实际由Query获取的列列表中,因此不会影响结果。但是,在使用Query.statement访问器时,这些列会被传递。

自版本 2.0 起已弃用:此逻辑已弃用,并将在 SQLAlchemy 2.0 中删除。请参阅仅选择实体时使用 DISTINCT 添加额外列以获取 2.0 版中此用例的描述。

另请参阅

Select.distinct() - v2 等效方法。

参数:

*expr

可选的列表达式。当存在时,PostgreSQL 方言将渲染DISTINCT ON (<expressions>)结构。

自 1.4 版弃用:在其他方言中使用*expr 已弃用,并将在将来的版本中引发CompileError

method enable_assertions(value: bool) → Self

控制是否生成断言。

当设置为 False 时,返回的查询在执行某些操作之前不会断言其状态,包括在调用filter()时未应用 LIMIT/OFFSET,在调用get()时不存在条件,以及在调用filter()/order_by()/group_by()时不存在“from_statement()”。 此更宽松的模式由自定义查询子类使用,以指定通常用法模式之外的条件或其他修改器。

应注意确保使用模式是可行的。 例如,由from_statement()应用的语句将覆盖filter()order_by()设置的任何条件。

method enable_eagerloads(value: bool) → Self

控制是否渲染急切连接和子查询。

当设置为 False 时,返回的查询不会渲染急切连接,而不管joinedload()subqueryload()选项或映射级别的lazy='joined'/lazy='subquery'配置。

主要用于将查询的语句嵌套到子查询或其他可选择项中,或者使用Query.yield_per()时。

method except_(*q: Query) → Self

对一个或多个查询产生此查询的差集。

Query.union()的工作方式相同。 有关用法示例,请参见该方法。

另请参阅

Select.except_() - v2 等效方法。

method except_all(*q: Query) → Self

对一个或多个查询产生此查询的全集。

Query.union()的工作方式相同。 有关用法示例,请参见该方法。

另请参阅

Select.except_all() - v2 等效方法。

method execution_options(**kwargs: Any) → Self

设置在执行期间生效的非 SQL 选项。

此处允许的选项包括Connection.execution_options()接受的所有选项,以及一系列 ORM 特定选项:

populate_existing=True - 相当于使用Query.populate_existing()

autoflush=True|False - 相当于使用Query.autoflush()

yield_per=<value> - 相当于使用Query.yield_per()

注意,如果使用Query.yield_per()方法或执行选项,则stream_results执行选项会自动启用。

1.4 版本中新增:- 在 Query.execution_options() 中添加了 ORM 选项

使用 2.0 风格 查询时,也可以在每次执行时指定执行选项,通过 Session.execution_options 参数。

警告

不应在单个 ORM 语句执行的级别使用 Connection.execution_options.stream_results 参数,因为 Session 不会跟踪来自单个会话中的不同模式转换映射的对象。对于在单个 Session 范围内的多个模式转换映射,请参阅水平分片。

另请参阅

使用服务器端游标(即流式结果)

Query.get_execution_options()

Select.execution_options() - v2 相当的方法。

method exists() → Exists

一个方便的方法,将查询转换为 EXISTS 子查询的形式 EXISTS (SELECT 1 FROM … WHERE …)。

例如:

q = session.query(User).filter(User.name == 'fred')
session.query(q.exists())

生成类似于以下 SQL:

SELECT EXISTS (
 SELECT 1 FROM users WHERE users.name = :name_1
) AS anon_1

EXISTS 构造通常用于 WHERE 子句中:

session.query(User.id).filter(q.exists()).scalar()

请注意,一些数据库(如 SQL Server)不允许在 SELECT 的列子句中出现 EXISTS 表达式。要根据 EXISTS 在 WHERE 中作为 WHERE 子句的简单布尔值选择,请使用 literal()

from sqlalchemy import literal

session.query(literal(True)).filter(q.exists()).scalar()

另请参阅

Select.exists() - v2 相当的方法。

method filter(*criterion: _ColumnExpressionArgument[bool]) → Self

将给定的过滤条件应用于此 Query 的副本,使用 SQL 表达式。

例如:

session.query(MyClass).filter(MyClass.name == 'some name')

可以指定多个条件,用逗号分隔;效果是它们将使用 and_() 函数连接在一起:

session.query(MyClass).\
 filter(MyClass.name == 'some name', MyClass.id > 5)

条件可以是任何适用于 select WHERE 子句的 SQL 表达式对象。字符串表达式会通过 text() 构造转换为 SQL 表达式结构。

另请参阅

Query.filter_by() - 使用关键字表达式进行过滤。

Select.where() - v2 相当的方法。

method filter_by(**kwargs: Any) → Self

将给定的过滤条件应用于此Query的副本,使用关键字表达式。

例如:

session.query(MyClass).filter_by(name = 'some name')

可以指定多个条件,用逗号分隔;效果是它们将使用and_()函数连接在一起:

session.query(MyClass).\
 filter_by(name = 'some name', id = 5)

关键字表达式是从查询的主要实体中提取的,或者是最后一个被调用Query.join()的目标实体。

另请参阅

Query.filter() - 根据 SQL 表达式进行过滤。

Select.filter_by() - v2 相当的方法。

method first() → _T | None

返回此Query的第一个结果,如果结果不包含任何行,则返回None

first()在生成的 SQL 中应用了一个限制为一的限制,因此只在服务器端生成一个主实体行(请注意,如果存在联接加载的集合,则可能由多个结果行组成)。

调用Query.first()会导致基础查询的执行。

另请参阅

Query.one()

Query.one_or_none()

Result.first() - v2 相当的方法。

Result.scalars() - v2 相当的方法。

method from_statement(statement: ExecutableReturnsRows) → Self

执行给定的 SELECT 语句并返回结果。

此方法绕过所有内部语句编译,并且语句在不经修改的情况下执行。

语句通常是text()select()构造,并且应返回适合此Query所代表的实体类的列集。

另请参阅

Select.from_statement() - v2 相当的方法。

method get(ident: _PKIdentityArgument) → Any | None

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

自版本 2.0 起弃用:Query.get() 方法被认为是 SQLAlchemy 1.x 系列的遗留功能,并在 2.0 版本中成为遗留构造。该方法现在作为 Session.get() 可用(有关 SQLAlchemy 2.0 的背景,请参见:SQLAlchemy 2.0 - 主要迁移指南)

例如:

my_user = session.query(User).get(5)

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

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

Query.get() 在提供对所属 Session 的标识映射的直接访问方面是特殊的。如果给定的主键标识符存在于本地标识映射中,则对象将直接从该集合返回,而不会发出 SQL,除非对象已被标记为完全过期。如果不存在,则执行 SELECT 以定位对象。

Query.get() 也会检查对象是否存在于标识映射中并标记为过期 - 发出一个 SELECT 来刷新对象以及确保行仍然存在。如果不是,ObjectDeletedError 被引发。

Query.get() 仅用于返回单个映射实例,而不是多个实例或单个列构造,并且严格地基于单个主键值。源 Query 必须以这种方式构造,即针对单个映射实体,没有额外的过滤条件。但是,可以通过Query.options() 应用加载选项,并且如果对象尚未在本地存在,则将使用该选项。

参数:

ident

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

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

my_object = query.get(5)

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

my_object = query.get((5, 10))

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

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

版本 1.3 中的新功能:Query.get() 方法现在可选地接受属性名称到值的字典,以指示主键标识符。

返回:

对象实例,或 None

method get_children(*, omit_attrs: Tuple[str, ...] = (), **kw: Any) → Iterable[HasTraverseInternals]

继承自 HasTraverseInternals.get_children() 方法的 HasTraverseInternals

返回此 HasTraverseInternals 的直接子级 HasTraverseInternals 元素。

这用于访问遍历。

**kw 可能包含改变返回的集合的标志,例如为了减少更大的遍历而返回子集,或者从不同上下文(例如模式级别集合而不是从子句级别)返回子项。

method get_execution_options() → _ImmutableExecuteOptions

获取在执行期间生效的非 SQL 选项。

版本 1.3 中的新功能。

另请参阅

Query.execution_options()

Select.get_execution_options() - v2 可比较方法。

attribute get_label_style

检索当前的标签样式。

版本 1.4 中的新功能。

另请参阅

Select.get_label_style() - v2 等效方法。

method group_by(_Query__first: Literal[None, False, _NoArg.NO_ARG] | _ColumnExpressionOrStrLabelArgument[Any] = _NoArg.NO_ARG, *clauses: _ColumnExpressionOrStrLabelArgument[Any]) → Self

对查询应用一个或多个 GROUP BY 准则,并返回新生成的 Query

所有现有的 GROUP BY 设置都可以通过传递 None 来抑制 - 这也会抑制映射器上配置的任何 GROUP BY。

另请参阅

这些部分描述了 GROUP BY 的 2.0 风格 调用,但同样适用于 Query

带有 GROUP BY / HAVING 的聚合函数 - 在 SQLAlchemy 统一教程 中

按标签排序或分组 - 在 SQLAlchemy 统一教程 中

Select.group_by() - v2 等效方法。

method having(*having: _ColumnExpressionArgument[bool]) → Self

对查询应用 HAVING 准则,并返回新生成的 Query

Query.having()Query.group_by() 结合使用。

HAVING 条件使得可以对像 COUNT、SUM、AVG、MAX 和 MIN 这样的聚合函数使用过滤器,例如:

q = session.query(User.id).\
 join(User.addresses).\
 group_by(User.id).\
 having(func.count(Address.id) > 2)

另请参见

Select.having() - v2 等效方法。

method instances(result_proxy: CursorResult[Any], context: QueryContext | None = None) → Any

在给定 CursorResultQueryContext 的情况下返回 ORM 结果。

自版本 2.0 起已弃用:Query.instances() 方法已弃用,并将在以后的版本中删除。请改用 Select.from_statement() 方法或与 Session.execute() 结合使用的 aliased() 构造。

method intersect(*q: Query) → Self

对此查询与一个或多个查询进行 INTERSECT 操作。

Query.union() 的工作方式相同。查看该方法以获取使用示例。

另请参见

Select.intersect() - v2 等效方法。

method intersect_all(*q: Query) → Self

对此查询与一个或多个查询进行 INTERSECT ALL 操作。

Query.union() 的工作方式相同。查看该方法以获取使用示例。

另请参见

Select.intersect_all() - v2 等效方法。

attribute is_single_entity

指示此 Query 返回元组还是单个实体。

如果此查询为其结果列表中的每个实例返回单个实体,则返回 True,如果此查询为每个结果返回实体元组,则返回 False。

新版本 1.3.11 中新增。

另请参见

Query.only_return_tuples()

method join(target: _JoinTargetArgument, onclause: _OnClauseArgument | None = None, *, isouter: bool = False, full: bool = False) → Self

创建针对此 Query 对象的条件的 SQL JOIN,并以生成方式应用,返回新生成的 Query

简单关系连接

考虑两个类 UserAddress 之间的映射,其中关系 User.addresses 表示与每个 User 关联的 Address 对象的集合。Query.join() 的最常见用法是沿着这个关系创建 JOIN,使用 User.addresses 属性作为指示器来指示如何发生这种情况:

q = session.query(User).join(User.addresses)

在上面的例子中,沿着 User.addresses 调用 Query.join() 将导致大致等同于以下 SQL:

SELECT user.id, user.name
FROM user JOIN address ON user.id = address.user_id

在上面的例子中,我们将User.addresses称为传递给 Query.join() 的“on clause”,也就是说,它指示了“JOIN”的“ON”部分应如何构建。

要构建一系列连接,可以使用多个Query.join()调用。关系绑定的属性同时暗示连接的左侧和右侧:

q = session.query(User).\
 join(User.orders).\
 join(Order.items).\
 join(Item.keywords)

注意

如上例所示,每次调用 join()方法的顺序很重要。例如,如果我们在连接链中指定User、然后是Item、然后是Order,那么 Query 不会正确知道如何连接;在这种情况下,根据传递的参数,它可能会引发一个无法连接的错误,或者它可能会生成无效的 SQL,而数据库会引发一个错误。在正确的实践中,应以使得 JOIN 子句在 SQL 中呈现的方式调用Query.join()方法,并且每次调用应该表示与之前内容的清晰关联。

连接到目标实体或可选择的

Query.join()的第二种形式允许任何映射实体或核心可选择的构造作为目标。在这种用法中,Query.join()将尝试沿着两个实体之间的自然外键关系创建一个 JOIN:

q = session.query(User).join(Address)

在上述调用形式中,Query.join()被调用来自动为我们创建“on 子句”。如果两个实体之间没有外键,或者如果目标实体和左侧已存在的实体之间有多个外键链接,以至于创建连接需要更多信息,则此调用形式最终将引发错误。请注意,在指示对没有任何 ON 子句的目标的连接时,不会考虑 ORM 配置的关系。

连接到具有 ON 子句的目标

第三种调用形式允许显式传递目标实体以及 ON 子句。包含 SQL 表达式作为 ON 子句的示例如下所示:

q = session.query(User).join(Address, User.id==Address.user_id)

上述形式也可以使用一个与关系绑定的属性作为 ON 子句:

q = session.query(User).join(Address, User.addresses)

上述语法对于我们希望连接到特定目标实体的别名的情况可能很有用。如果我们想要两次连接到Address,可以使用aliased()函数设置两个别名:

a1 = aliased(Address)
a2 = aliased(Address)

q = session.query(User).\
 join(a1, User.addresses).\
 join(a2, User.addresses).\
 filter(a1.email_address=='ed@foo.com').\
 filter(a2.email_address=='ed@bar.com')

与关系绑定的调用形式还可以使用PropComparator.of_type()方法指定目标实体;一个与上述相同的查询将是:

a1 = aliased(Address)
a2 = aliased(Address)

q = session.query(User).\
 join(User.addresses.of_type(a1)).\
 join(User.addresses.of_type(a2)).\
 filter(a1.email_address == 'ed@foo.com').\
 filter(a2.email_address == 'ed@bar.com')

扩充内置的 ON 子句

作为为现有关系提供完整自定义 ON 条件的替代方法,可以将PropComparator.and_()函数应用于关系属性,以将其他条件合并到 ON 子句中;其他条件将与默认条件使用 AND 组合:

q = session.query(User).join(
 User.addresses.and_(Address.email_address != 'foo@bar.com')
)

1.4 版本中新增。

加入表格和子查询

加入的目标也可以是任何表格或 SELECT 语句,它可以与目标实体相关联,也可以不相关联。使用适当的.subquery()方法以将查询转换为子查询:

subq = session.query(Address).\
 filter(Address.email_address == 'ed@foo.com').\
 subquery()

q = session.query(User).join(
 subq, User.id == subq.c.user_id
)

通过使用aliased()将子查询链接到实体,可以在特定关系和/或目标实体方面加入子查询:

subq = session.query(Address).\
 filter(Address.email_address == 'ed@foo.com').\
 subquery()

address_subq = aliased(Address, subq)

q = session.query(User).join(
 User.addresses.of_type(address_subq)
)

控制加入来源

在当前Query状态的左侧与我们想要加入的内容不一致的情况下,可以使用Query.select_from()方法:

q = session.query(Address).select_from(User).\
 join(User.addresses).\
 filter(User.name == 'ed')

这将产生类似于以下的 SQL:

SELECT address.* FROM user
 JOIN address ON user.id=address.user_id
 WHERE user.name = :name_1

另请参阅

Select.join() - v2 等效方法。

参数:

  • *propsQuery.join()的传入参数,现代用法中的 props 集合应被视为一种或两种参数形式,要么是一个“目标”实体或 ORM 属性绑定的关系,要么是一个目标实体加上一个“on 子句”,该子句可以是 SQL 表达式或 ORM 属性绑定的关系。

  • isouter=False – 如果为 True,则使用的连接将是左外连接,就像调用Query.outerjoin()方法一样。

  • full=False – 渲染 FULL OUTER JOIN;意味着isouter

method label(name: str | None) → Label[Any]

返回由此Query表示的完整 SELECT 语句,转换为具有给定名称标签的标量子查询。

另请参阅

Select.label() - v2 可比较方法。

attribute lazy_loaded_from

使用此Query进行惰性加载操作的InstanceState

自 1.4 版本起弃用:此属性应通过ORMExecuteState.lazy_loaded_from属性查看,在SessionEvents.do_orm_execute()事件的上下文中。

另请参阅

ORMExecuteState.lazy_loaded_from

method limit(limit: _LimitOffsetType) → Self

LIMIT应用于查询并返回新结果的Query

请参阅

Select.limit() - v2 等效方法。

method merge_result(iterator: FrozenResult[Any] | Iterable[Sequence[Any]] | Iterable[object], load: bool = True) → FrozenResult[Any] | Iterable[Any]

将一个结果合并到这个Query对象的会话中。

自 2.0 版开始弃用:Query.merge_result()方法被认为是 SQLAlchemy 1.x 系列的遗留方法,并在 2.0 版中成为遗留构造。 该方法被merge_frozen_result()函数取代。(有关 SQLAlchemy 2.0 的背景,请参阅:SQLAlchemy 2.0 - 主要迁移指南)

给定与此相同结构的Query返回的迭代器,返回一个相同的结果迭代器,所有映射实例都使用Session.merge()合并到会话中。 这是一种优化方法,将合并所有映射实例,保留结果行的结构和未映射列,比直接为每个值显式调用Session.merge()方法的方法开销小。

结果的结构是基于这个Query的列列表来确定的 - 如果它们不对应,将会发生未经检查的错误。

‘load’参数与Session.merge()相同。

有关如何使用Query.merge_result()的示例,请参阅示例 Dogpile Caching 的源代码,其中Query.merge_result()用于有效地从缓存中恢复状态到目标Session中。

method offset(offset: _LimitOffsetType) → Self

OFFSET应用于查询并返回新结果的Query

请参阅

Select.offset() - v2 等效方法。

method one() → _T

返回一个结果或引发异常。

如果查询未选择任何行,则引发sqlalchemy.orm.exc.NoResultFound。 如果返回多个对象标识,或者如果返回多行用于仅返回标量值而不是完整身份映射实体的查询,则引发sqlalchemy.orm.exc.MultipleResultsFound

调用one()会执行基础查询。

另请参阅

Query.first()

Query.one_or_none()

Result.one() - v2 可比较的方法。

Result.scalar_one() - v2 可比较的方法。

method one_or_none() → _T | None

返回最多一个结果或引发异常。

如果查询不选择任何行,则返回None。如果返回了多个对象标识或者对于只返回标量值而不是完整身份映射实体的查询返回了多行,则会引发sqlalchemy.orm.exc.MultipleResultsFound异常。

调用Query.one_or_none()会执行基础查询。

另请参阅

Query.first()

Query.one()

Result.one_or_none() - v2 可比较的方法。

Result.scalar_one_or_none() - v2 可比较的方法。

method only_return_tuples(value: bool) → Query

当设置为 True 时,查询结果将始终是一个Row对象。

这可以将通常返回标量的单个实体的查询更改为在所有情况下返回Row结果。

另请参阅

Query.tuples() - 返回元组,但在类型级别上也将结果类型化为Tuple

Query.is_single_entity()

Result.tuples() - v2 可比较的方法。

method options(*args: ExecutableOption) → Self

返回一个新的Query对象,应用给定的映射器选项列表。

大多数提供的选项涉及更改如何加载列和关系映射的属性。

另请参阅

列加载选项

使用加载器选项进行关系加载

method order_by(_Query__first: Literal[None, False, _NoArg.NO_ARG] | _ColumnExpressionOrStrLabelArgument[Any] = _NoArg.NO_ARG, *clauses: _ColumnExpressionOrStrLabelArgument[Any]) → Self

对查询应用一个或多个 ORDER BY 条件,并返回新生成的Query

例如:

q = session.query(Entity).order_by(Entity.id, Entity.name)

多次调用此方法等同于一次调用并连接所有子句。所有现有的 ORDER BY 条件可以通过仅传递 None 来取消。然后可以通过再次调用 Query.order_by() 来添加新的 ORDER BY 条件,例如:

# will erase all ORDER BY and ORDER BY new_col alone
q = q.order_by(None).order_by(new_col)

另见

这些部分以 2.0 风格 调用 ORDER BY 描述,但也适用于 Query

ORDER BY - 在 SQLAlchemy 统一教程 中

按标签排序或分组 - 在 SQLAlchemy 统一教程 中

Select.order_by() - v2 等效方法。

method outerjoin(target: _JoinTargetArgument, onclause: _OnClauseArgument | None = None, *, full: bool = False) → Self

对此 Query 对象的条件创建一个左外连接,并生成应用,返回新生成的 Query

用法与 join() 方法相同。

另见

Select.outerjoin() - v2 等效方法。

method params(_Query__params: Dict[str, Any] | None = None, **kw: Any) → Self

为在 filter() 中指定的绑定参数添加值。

参数可以使用 **kwargs 指定,或者作为第一个位置参数使用一个可选的字典。两者之所以同时存在是因为 **kwargs 方便,但是一些参数字典包含 unicode 键,此时无法使用 **kwargs。

method populate_existing() → Self

返回一个 Query,它将在加载时使所有实例过期并刷新,或从当前 Session 复用。

自 SQLAlchemy 1.4 起,Query.populate_existing() 方法等同于在 ORM 级别使用 populate_existing 执行选项。有关此选项的更多背景,请参见 Populate Existing 部分。

method prefix_with(*prefixes: _TextCoercedExpressionArgument[Any], dialect: str = '*') → Self

继承自 HasPrefixes.prefix_with() 方法的 HasPrefixes

在语句关键字之后添加一个或多个表达式,即 SELECT、INSERT、UPDATE 或 DELETE。生成的。

这用于支持特定于后端的前缀关键字,例如 MySQL 提供的关键字。

例如:

stmt = table.insert().prefix_with("LOW_PRIORITY", dialect="mysql")

# MySQL 5.7 optimizer hints
stmt = select(table).prefix_with(
 "/*+ BKA(t1) */", dialect="mysql")

可以通过多次调用 HasPrefixes.prefix_with() 来指定多个前缀。

参数:

  • *prefixes – 文本或ClauseElement构造,将在 INSERT、UPDATE 或 DELETE 关键字之后呈现。

  • dialect – 可选的字符串方言名称,将限制仅将此前缀呈现到该方言。

method reset_joinpoint() → Self

返回一个新的Query,其中“连接点”已重置回查询的基本 FROM 实体。

此方法通常与Query.join()方法的aliased=True特性一起使用。有关其用法,请参见Query.join()中的示例。

method scalar() → Any

返回第一个结果的第一个元素,如果没有行,则返回 None。如果返回多行,则引发 MultipleResultsFound。

>>> session.query(Item).scalar()
<Item>
>>> session.query(Item.id).scalar()
1
>>> session.query(Item.id).filter(Item.id < 0).scalar()
None
>>> session.query(Item.id, Item.name).scalar()
1
>>> session.query(func.count(Parent.id)).scalar()
20

这会导致底层查询的执行。

另请参阅

Result.scalar() - v2 可比较的方法。

method scalar_subquery() → ScalarSelect[Any]

返回由此Query表示的完整 SELECT 语句,转换为标量子查询。

类似于SelectBase.scalar_subquery()

在 1.4 版本中更改:Query.scalar_subquery()方法替换了Query.as_scalar()方法。

另请参阅

Select.scalar_subquery() - v2 可比较的方法。

method select_from(*from_obj: FromClauseRole | Type[Any] | Inspectable[_HasClauseElement[Any]] | _HasClauseElement[Any]) → Self

明确设置此Query的 FROM 子句。

Query.select_from()通常与Query.join()结合使用,以控制在连接的“左”侧选择哪个实体。

此处的实体或可选择对象有效地替换了任何对Query.join()的调用的“左边缘”,否则,当没有其他方式建立连接点时,通常默认的“连接点”是Query对象的实体列表中的最左边的实体。

典型的例子:

q = session.query(Address).select_from(User).\
 join(User.addresses).\
 filter(User.name == 'ed')

这会产生等效于以下 SQL 的结果:

SELECT address.* FROM user
JOIN address ON user.id=address.user_id
WHERE user.name = :name_1

参数:

*from_obj – 用于应用于 FROM 子句的一个或多个实体的集合。实体可以是映射类,AliasedClass 对象,Mapper 对象以及核心 FromClause 元素,如子查询。

另请参阅

Query.join() - Query.join() 方法。

Query.select_entity_from()

Select.select_from() - v2 相关的方法。

attribute selectable

返回由此 Query 发出的 Select 对象。

用于 inspect() 兼容性,这相当于:

query.enable_eagerloads(False).with_labels().statement
method set_label_style(style: SelectLabelStyle) → Self

将列标签应用于 Query.statement 的返回值。

表示此 Query 的语句访问器应返回一个 SELECT 语句,该语句对所有列应用标签的形式为 _;这通常用于消除具有相同名称的多个表的列的歧义性。

当 Query 实际发出 SQL 加载行时,它总是使用列标签。

注意

Query.set_label_style() 方法适用于 Query.statement 的输出,适用于 Query 本身的任何结果行调用系统,例如 Query.first()Query.all() 等。要使用 Query.set_label_style() 执行查询,请使用 Session.execute() 调用 Query.statement

result = session.execute(
 query
 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)
 .statement
)

版本 1.4 中的新功能。

另请参阅

Select.set_label_style() - v2 相关的方法。

method slice(start: int, stop: int) → Self

计算由给定索引表示的 Query 的“切片”,并返回生成的 Query

开始和停止索引的行为类似于 Python 内置函数 range() 的参数。此方法提供了一种替代使用 LIMIT/OFFSET 来获取查询切片的方法。

例如,

session.query(User).order_by(User.id).slice(1, 3)

渲染为

SELECT  users.id  AS  users_id,
  users.name  AS  users_name
FROM  users  ORDER  BY  users.id
LIMIT  ?  OFFSET  ?
(2,  1)

另请参阅

Query.limit()

Query.offset()

Select.slice() - v2 等效方法。

attribute statement

由此查询表示的完整 SELECT 语句。

默认情况下,语句不会对构造应用歧义性标签,除非首先调用with_labels(True)

method subquery(name: str | None = None, with_labels: bool = False, reduce_columns: bool = False) → Subquery

返回由此Query表示的完整 SELECT 语句,嵌入在Alias中。

查询中的急切 JOIN 生成被禁用。

另请参阅

Select.subquery() - v2 可比较方法。

参数:

  • name – 要分配为别名的字符串名称;这将通过FromClause.alias()传递。如果为None,则在编译时将确定性地生成一个名称。

  • with_labels – 如果为 True,则首先将在Query上调用with_labels(),以将表限定标签应用于所有列。

  • reduce_columns – 如果为 True,则将在生成的select()构造上调用Select.reduce_columns(),以删除通过外键或 WHERE 子句等价关系相互引用的同名列。

method suffix_with(*suffixes: _TextCoercedExpressionArgument[Any], dialect: str = '*') → Self

继承自 HasSuffixes.suffix_with() 方法的 HasSuffixes

在语句后添加一个或多个表达式。

这用于支持特定于后端的后缀关键字在某些构造上。

例如:

stmt = select(col1, col2).cte().suffix_with(
 "cycle empno set y_cycle to 1 default 0", dialect="oracle")

可以通过多次调用HasSuffixes.suffix_with()来指定多个后缀。

参数:

  • *suffixes – 文本或ClauseElement构造,将在目标子句后呈现。

  • dialect – 可选的字符串方言名称,它将限制此后缀的呈现仅限于该方言。

method tuples() → Query

返回此 Query 的元组形式。

该方法调用了 Query.only_return_tuples() 方法,并将其值设为 True,这本身就确保了这个 Query 将始终返回 Row 对象,即使查询只针对单个实体也是如此。它还在类型级别返回一个“类型化”的查询,如果可能的话,将结果行类型化为带有类型的 Tuple 对象。

此方法可以与 Result.tuples() 方法进行比较,后者返回“self”,但从类型的角度来看,返回一个对象,该对象将为结果生成带有类型的 Tuple 对象。仅当此 Query 对象已经是一个类型化的查询对象时,类型才会生效。

2.0 版本中的新功能。

另请参阅

Result.tuples() - v2 等效方法。

method union(*q: Query) → Self

生成该查询与一个或多个查询的 UNION。

例如:

q1 = sess.query(SomeClass).filter(SomeClass.foo=='bar')
q2 = sess.query(SomeClass).filter(SomeClass.bar=='foo')

q3 = q1.union(q2)

该方法接受多个查询对象,以控制嵌套级别。例如一系列的 union() 调用:

x.union(y).union(z).all()

将在每个 union() 上进行嵌套,并产生:

SELECT * FROM (SELECT * FROM (SELECT * FROM X UNION
 SELECT * FROM y) UNION SELECT * FROM Z)

而:

x.union(y, z).all()

产生:

SELECT * FROM (SELECT * FROM X UNION SELECT * FROM y UNION
 SELECT * FROM Z)

请注意,许多数据库后端不允许在 UNION、EXCEPT 等中调用的查询上渲染 ORDER BY。要禁用所有 ORDER BY 子句,包括在映射器上配置的子句,请发出 query.order_by(None) - 结果的 Query 对象不会在其 SELECT 语句中渲染 ORDER BY。

另请参阅

Select.union() - v2 等效方法。

method union_all(*q: Query) → Self

生成该查询与一个或多个查询的 UNION ALL。

Query.union() 的工作方式相同。查看该方法以获取用法示例。

另请参阅

Select.union_all() - v2 等效方法。

method update(values: Dict[_DMLColumnArgument, Any], synchronize_session: SynchronizeSessionArgument = 'auto', update_args: Dict[Any, Any] | None = None) → int

使用任意 WHERE 子句执行 UPDATE。

更新数据库中与此查询匹配的行。

例如:

sess.query(User).filter(User.age == 25).\
 update({User.age: User.age - 10}, synchronize_session=False)

sess.query(User).filter(User.age == 25).\
 update({"age": User.age - 10}, synchronize_session='evaluate')

警告

查看 ORM-启用的 INSERT、UPDATE 和 DELETE 语句部分以获取重要的注意事项和警告,包括在使用任意 UPDATE 和 DELETE 与映射器继承配置时的限制。

参数:

  • values – 一个带有属性名的字典,或者作为键的映射属性或 SQL 表达式,以及作为值的文字值或 SQL 表达式。 如果需要参数顺序模式,则可以将值作为 2 元组列表传递; 这需要还将 update.preserve_parameter_order 标志传递给 Query.update.update_args 字典。

  • synchronize_session – 选择更新会话中对象属性的策略。 有关这些策略的讨论,请参阅 启用 ORM 的 INSERT、UPDATE 和 DELETE 语句 部分。

  • update_args – 可选字典,如果存在,则作为对象的 **kw 传递给底层的 update() 构造。 可以用于传递特定于方言的参数,如 mysql_limit,以及其他特殊参数,如 update.preserve_parameter_order

返回:

数据库的“行计数”功能返回的匹配行数。

另请参阅

启用 ORM 的 INSERT、UPDATE 和 DELETE 语句

method value(column: _ColumnExpressionArgument[Any]) → Any

返回与给定列表对应的标量结果。

自版本 1.4 起弃用:Query.value()已弃用,将在将来的版本中删除。 请使用 Query.with_entities()Query.scalar() 结合使用。

method values(*columns: _ColumnsClauseArgument[Any]) → Iterable[Any]

返回一个迭代器,产生与给定列列表对应的结果元组

自版本 1.4 起弃用:Query.values()已弃用,将在将来的版本中删除。 请使用 Query.with_entities()

method where(*criterion: _ColumnExpressionArgument[bool]) → Self

Query.filter()的同义词。

版本 1.4 中的新功能。

另请参阅

Select.where() - v2 等效方法。

attribute whereclause

只读属性,返回此查询的当前 WHERE 条件。

此返回值是一个 SQL 表达式构造,如果没有建立条件,则为 None

另请参阅

Select.whereclause - v2 等效属性。

method with_entities(*entities: _ColumnsClauseArgument[Any], **_Query__kw: Any) → Query[Any]

返回一个用给定实体替换 SELECT 列的新 Query

例如:

# Users, filtered on some arbitrary criterion
# and then ordered by related email address
q = session.query(User).\
 join(User.address).\
 filter(User.name.like('%ed%')).\
 order_by(Address.email)

# given *only* User.id==5, Address.email, and 'q', what
# would the *next* User in the result be ?
subq = q.with_entities(Address.email).\
 order_by(None).\
 filter(User.id==5).\
 subquery()
q = q.join((subq, subq.c.email < Address.email)).\
 limit(1)

另请参阅

Select.with_only_columns() - v2 可比较的方法。

method with_for_update(*, nowait: bool = False, read: bool = False, of: _ForUpdateOfArgument | None = None, skip_locked: bool = False, key_share: bool = False) → Self

返回一个带有指定选项的新 Query,用于 FOR UPDATE 子句。

此方法的行为与 GenerativeSelect.with_for_update() 相同。当不带参数调用时,生成的 SELECT 语句将附加一个 FOR UPDATE 子句。当指定其他参数时,后端特定选项,如 FOR UPDATE NOWAITLOCK IN SHARE MODE 可以生效。

例如:

q = sess.query(User).populate_existing().with_for_update(nowait=True, of=User)

在 PostgreSQL 后端上执行上述查询将呈现如下:

SELECT users.id AS users_id FROM users FOR UPDATE OF users NOWAIT

警告

在急加载关系的上下文中使用 with_for_update 不受 SQLAlchemy 官方支持或推荐,并且可能无法与各种数据库后端上的某些查询一起使用。当成功使用 with_for_update 与涉及 joinedload() 的查询时,SQLAlchemy 将尝试发出锁定所有涉及表的 SQL。

注意

通常建议在使用 Query.with_for_update() 方法时结合使用 Query.populate_existing() 方法。Query.populate_existing() 的目的是强制将从 SELECT 中读取的所有数据填充到返回的 ORM 对象中,即使这些对象已经在标识映射中。

另请参阅

GenerativeSelect.with_for_update() - 具有完整参数和行为描述的核心级方法。

Query.populate_existing() - 覆盖已加载到标识映射中的对象的属性。

method with_hint(selectable: _FromClauseArgument, text: str, dialect_name: str = '*') → Self

继承自 HasHints.with_hint() 方法的 HasHints 方法

为给定的可选择对象添加索引或其他执行上下文提示到此 Select 或其他可选择对象。

提示文本在使用的数据库后端的适当位置呈现,相对于作为 selectable 参数传递的给定 TableAlias。方言实现通常使用 Python 字符串替换语法,其中的令牌 %(name)s 用于呈现表或别名的名称。例如,当使用 Oracle 时,以下内容:

select(mytable).\
 with_hint(mytable, "index(%(name)s ix_mytable)")

SQL 渲染如下:

select /*+ index(mytable ix_mytable) */ ... from mytable

dialect_name 选项将限制特定后端的特定提示的呈现。例如,同时为 Oracle 和 Sybase 添加提示:

select(mytable).\
 with_hint(mytable, "index(%(name)s ix_mytable)", 'oracle').\
 with_hint(mytable, "WITH INDEX ix_mytable", 'mssql')

也请参见

Select.with_statement_hint()

method with_labels() → Self

自 2.0 版本起已废弃:Query.with_labels()Query.apply_labels() 方法被认为是 SQLAlchemy 1.x 系列的遗留代码,并在 2.0 版本中成为遗留构造。请改用 set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL)。 (关于 SQLAlchemy 2.0 的背景信息请参见:SQLAlchemy 2.0 - 主要迁移指南)

method with_parent(instance: object, property: attributes.QueryableAttribute[Any] | None = None, from_entity: _ExternalEntityType[Any] | None = None) → Self

添加筛选条件,将给定实例与子对象或集合关联起来,同时使用其属性状态和已建立的 relationship() 配置。

自 2.0 版本起已废弃:Query.with_parent() 方法被认为是 SQLAlchemy 1.x 系列的遗留代码,并在 2.0 版本中成为遗留构造。请使用独立构造 with_parent()。(关于 SQLAlchemy 2.0 的背景信息请参见:SQLAlchemy 2.0 - 主要迁移指南)

该方法使用 with_parent() 函数生成子句,其结果传递给 Query.filter()

参数与 with_parent() 相同,但给定的属性可以为 None,在这种情况下,将对此 Query 对象的目标映射执行搜索。

参数:

  • instance – 一个具有一些 relationship() 的实例。

  • property – 类绑定属性,指示应从实例中使用哪个关系来协调父/子关系。

  • from_entity – 将其视为左侧的实体。 默认为 Query 本身的“零”实体。

method with_session(session: Session) → Self

返回一个将使用给定 SessionQuery

虽然通常使用 Session.query() 方法来实例化 Query 对象,但也可以直接构建 Query 对象,而无需必须使用 Session。 这样的 Query 对象,或者已经与不同 Session 关联的任何 Query,可以使用这种方法产生与目标会话相关联的新的 Query 对象:

from sqlalchemy.orm import Query

query = Query([MyClass]).filter(MyClass.id == 5)

result = query.with_session(my_session).one()
method with_statement_hint(text: str, dialect_name: str = '*') → Self

继承自 HasHints.with_statement_hint() 方法的 HasHints

向此 Select 或其他可选择对象添加语句提示。

此方法类似于 Select.with_hint(),但不需要单独的表,而是适用于整个语句。

此处的提示是特定于后端数据库的,并且可能包括诸如隔离级别、文件指令、提取指令等的指令。

另见

Select.with_hint()

Select.prefix_with() - 通用的 SELECT 前缀,也可以适用于某些特定于数据库的 HINT 语法,例如 MySQL 优化器提示

method with_transformation(fn: Callable[[Query], Query]) → Query

返回一个通过给定函数转换的新的 Query 对象。

例如:

def filter_something(criterion):
 def transform(q):
 return q.filter(criterion)
 return transform

q = q.with_transformation(filter_something(x==5))

这允许为 Query 对象创建临时配方。

method yield_per(count: int) → Self

每次仅产生 count 行。

此方法的目的是在获取非常大的结果集(> 10K 行)时,将结果批处理为子集合并部分地输出它们,以便 Python 解释器不需要声明非常大的内存区域,这既费时又导致内存使用过多。 当使用适当的 yield-per 设置时(例如,大约为 1000),即使使用缓冲行的 DBAPI(大多数情况下),从获取数十万行的性能也经常会翻倍。

从 SQLAlchemy 1.4 开始,Query.yield_per() 方法等同于在 ORM 级别使用 yield_per 执行选项。有关此选项的更多背景信息,请参见 使用 Yield Per 获取大型结果集 部分。

另请参见

使用 Yield Per 获取大型结果集

ORM 特定查询构造

本节已移至 附加 ORM API 构造。

使用会话

原文:docs.sqlalchemy.org/en/20/orm/session.html

在 ORM 映射类配置 中描述的声明基础和 ORM 映射函数是 ORM 的主要配置接口。一旦配置了映射,持久性操作的主要使用接口是 Session

  • 会话基础知识

    • 会话是做什么的?

    • 使用会话的基础知识

      • 打开和关闭会话

      • 构建开始/提交/回滚块

      • 使用 sessionmaker

      • 查询

      • 添加新的或现有的项目

      • 删除

      • 刷新

      • 按主键获取

      • 过期/刷新

      • 使用任意 WHERE 子句进行 UPDATE 和 DELETE

      • 自动开始

      • 提交

      • 回滚

      • 关闭

    • 会话常见问题

      • 何时创建 sessionmaker

      • 何时构建 Session,何时提交,何时关闭?

      • 会话是缓存吗?

      • 如何获取特定对象的 Session

      • 会话是线程安全的吗?AsyncSession 在并发任务中安全共享吗?

  • 状态管理

    • 对象状态简介

      • 获取对象的当前状态
    • 会话属性

    • 会话引用行为

    • 合并

      • 合并提示
    • 清除

    • 刷新/过期

      • 实际加载了什么

      • 何时使对象过期或刷新

  • 级联操作

    • 保存更新

      • 双向关系中保存更新级联的行为
    • 删除

      • 使用删除级联与多对多关系

      • 使用 ORM 关系的外键 ON DELETE 级联

      • 使用外键 ON DELETE 与多对多关系

    • 删除孤儿

    • 合并

    • 刷新过期

    • 清除

    • 关于删除 - 删除从集合和标量关系引用的对象的注意事项

  • 事务和连接管理

    • 管理事务

      • 使用 SAVEPOINT

      • 会话级别与引擎级别的事务控制

      • 显式开始

      • 启用两阶段提交

      • 设置事务隔离级别 / DBAPI 自动提交

      • 通过事件跟踪事务状态

    • 将会话加入外部事务(例如用于测试套件)

  • 其他持久化技术

    • 将 SQL 插入/更新表达式嵌入到刷新中

    • 在会话中使用 SQL 表达式

    • 强制将具有默认值的列设置为 NULL

    • 获取服务器生成的默认值

      • 情况 1:非主键,支持 RETURNING 或等效

      • 情况 2:表包含与 RETURNING 不兼容的触发器生成值

      • 情况 3:不支持或不需要非主键、RETURNING 或等效项

      • 情况 4:支持主键、RETURNING 或等效项

      • 情况 5:不支持主键、RETURNING 或等效项

      • 关于急切获取用于 INSERT 或 UPDATE 的客户端调用的 SQL 表达式的注意事项

    • 使用 INSERT、UPDATE 和 ON CONFLICT(即 upsert)返回 ORM 对象

      • 使用 PostgreSQL ON CONFLICT 和 RETURNING 返回 upserted ORM 对象
    • 分区策略(例如每个会话多个数据库后端)

      • 简单垂直分区

      • 为多引擎会话协调事务

      • 自定义垂直分区

      • 水平分区

    • 批量操作

  • 上下文/线程本地会话

    • 隐式方法访问

    • 线程本地作用域

    • 在 Web 应用程序中使用线程本地作用域

    • 使用自定义创建的作用域

    • 上下文会话 API

      • scoped_session

      • ScopedRegistry

      • ThreadLocalRegistry

      • QueryPropertyDescriptor

  • 使用事件跟踪查询、对象和会话变更

    • 执行事件

      • 基本查询拦截

      • 添加全局 WHERE / ON 条件

      • 重新执行语句

    • 持久性事件

      • before_flush()

      • after_flush()

      • after_flush_postexec()

      • Mapper 级刷新事件

    • 对象生命周期事件

      • 瞬态

      • Transient to Pending

      • Pending to Persistent

      • Pending to Transient

      • Loaded as Persistent

      • Persistent to Transient

      • Persistent to Deleted

      • Deleted to Detached

      • Persistent to Detached

      • Detached to Persistent

      • Deleted to Persistent

    • 事务事件

    • 属性更改事件

  • 会话 API

    • Session and sessionmaker()

      • sessionmaker

      • ORMExecuteState

      • Session

      • SessionTransaction

      • SessionTransactionOrigin

    • Session Utilities

      • close_all_sessions()

      • make_transient()

      • make_transient_to_detached()

      • object_session()

      • was_deleted()

    • 属性和状态管理工具

      • object_state()

      • del_attribute()

      • get_attribute()

      • get_history()

      • init_collection()

      • flag_modified()

      • flag_dirty()

      • instance_state()

      • is_instrumented()

      • set_attribute()

      • set_committed_value()

      • History

会话基础

原文:docs.sqlalchemy.org/en/20/orm/session_basics.html

会话的作用是什么?

从最一般的意义上讲,Session 建立了与数据库的所有交流,并代表了其生命周期内加载或关联的所有对象的“存储区”。它提供了一个接口,用于进行 SELECT 和其他查询,这些查询将返回并修改 ORM 映射的对象。ORM 对象本身保存在 Session 内部,位于一个称为 identity map 的结构中 - 这是一种维护每个对象唯一副本的数据结构,其中“唯一”意味着“只有一个具有特定主键的对象”。

在其最常见的使用模式中,Session 以大多数无状态形式开始。一旦发出查询或使用其他对象进行持久化,它将从与 Session 关联的 Engine 请求连接资源,然后在该连接上建立事务。该事务保持生效直到 Session 被指示提交或回滚事务。当事务结束时,与 Engine 关联的连接资源将被 释放 到引擎管理的连接池中。然后,使用新的连接检出开始新的事务。

Session 维护的 ORM 对象被 instrumented 以便每当 Python 程序中的属性或集合被修改时,都会生成一个变更事件,该事件会被 Session 记录下来。每当数据库即将被查询或事务即将被提交时,Session 首先 flushes 所有存储在内存中的待定更改到数据库中。这被称为 unit of work 模式。

当使用 Session 时,将其维护的 ORM 映射对象视为代理对象,它们对应于事务在 Session 中保持的本地数据库行。为了保持对象的状态与实际数据库中的状态相匹配,存在各种事件会导致对象重新访问数据库以保持同步。可以“分离”对象与 Session,并继续使用它们,尽管这种做法有其注意事项。通常情况下,当您想要再次使用它们时,您会重新将已分离的对象与另一个 Session 关联起来,以便它们可以恢复其表示数据库状态的正常任务。

使用会话的基础知识

这里介绍了最基本的 Session 使用模式。

打开和关闭会话

Session 可以单独构建,也可以使用 sessionmaker 类构建。通常,它会在开始时传递一个单独的 Engine 作为连接源。典型的用法可能如下所示:

from sqlalchemy import create_engine
from sqlalchemy.orm import Session

# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# create session and add objects
with Session(engine) as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()

在上述代码中,Session 是使用与特定数据库 URL 关联的 Engine 实例化的。然后,在 Python 上下文管理器(即 with: 语句)中使用它,以便在块结束时自动关闭;这相当于调用 Session.close() 方法。

Session.commit() 的调用是可选的,仅在我们与 Session 执行的工作包含要持久化到数据库的新数据时才需要。如果我们只发出 SELECT 调用并且不需要写入任何更改,则对 Session.commit() 的调用是不必要的。

注意

注意,在调用 Session.commit() 之后,无论是显式地还是在使用上下文管理器时,与 Session 关联的所有对象都会被过期,这意味着它们的内容将被清除以在下一个事务中重新加载。 如果这些对象被分离,除非使用 Session.expire_on_commit 参数来禁用此行为,否则它们将无法使用,直到与新的 Session 重新关联。 更多细节请参见提交部分。 ### 划定一个开始/提交/回滚块的框架

对于那些将要向数据库提交数据的情况,我们还可以将 Session.commit() 调用和整个事务的“框架”置于上下文管理器中。 在这里,“框架”意味着如果所有操作成功,则会调用 Session.commit() 方法,但如果引发任何异常,则会立即调用 Session.rollback() 方法,以便在将异常向外传播之前立即回滚事务。 在 Python 中,这主要是通过使用类似 try: / except: / else: 的代码块来表达的:

# verbose version of what a context manager will do
with Session(engine) as session:
    session.begin()
    try:
        session.add(some_object)
        session.add(some_other_object)
    except:
        session.rollback()
        raise
    else:
        session.commit()

通过使用 Session.begin() 方法返回的 SessionTransaction 对象可以更简洁地实现上述的长形操作序列,该对象为相同操作序列提供了上下文管理器接口:

# create session and add objects
with Session(engine) as session:
    with session.begin():
        session.add(some_object)
        session.add(some_other_object)
    # inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

更简洁地说,这两个上下文可以合并:

# create session and add objects
with Session(engine) as session, session.begin():
    session.add(some_object)
    session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

使用一个 sessionmaker

sessionmaker 的目的是为具有固定配置的 Session 对象提供一个工厂。 由于典型的应用程序会在模块范围内具有一个 Engine 对象,因此 sessionmaker 可以为与此引擎相对应的 Session 对象提供一个工厂:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources, typically in module scope
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()
# closes the session

sessionmaker 类似于 Engine,是一个模块级别的工厂,用于函数级别的会话 / 连接。因此,它也有自己的 sessionmaker.begin() 方法,类似于 Engine.begin(),它返回一个 Session 对象,并维护一个开始 / 提交 / 回滚块:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() and include begin()/commit()/rollback()
# at once
with Session.begin() as session:
    session.add(some_object)
    session.add(some_other_object)
# commits the transaction, closes the session

在上述 with: 块结束时,Session 的事务将提交,并且 Session 将关闭。

在编写应用程序时,sessionmaker 工厂应与 create_engine() 创建的 Engine 对象的范围相同,通常是模块级或全局范围。由于这些对象都是工厂,因此它们可以被任意数量的函数和线程同时使用。

另请参阅

sessionmaker

Session

查询

查询的主要方法是利用 select() 构造来创建一个 Select 对象,然后使用诸如 Session.execute()Session.scalars() 等方法执行该对象以返回结果。然后以 Result 对象的形式返回结果,包括 ScalarResult 等子变体。

SQLAlchemy ORM 查询的完整指南可在 ORM 查询指南 找到。以下是一些简要示例:

from sqlalchemy import select
from sqlalchemy.orm import Session

with Session(engine) as session:
    # query for ``User`` objects
    statement = select(User).filter_by(name="ed")

    # list of ``User`` objects
    user_obj = session.scalars(statement).all()

    # query for individual columns
    statement = select(User.name, User.fullname)

    # list of Row objects
    rows = session.execute(statement).all()

从 2.0 版本开始更改:现在采用“2.0”样式的查询作为标准。请参阅 2.0 迁移 - ORM 用法 获取从 1.x 系列迁移的注意事项。

另请参阅

ORM 查询指南 ### 添加新项目或现有项目

Session.add() 用于将实例放入会话中。对于暂时的(即全新的)实例,这将在下一次刷新时对这些实例执行插入操作。对于持久的(即由此会话加载的)实例,它们已经存在,不需要添加。已分离的(即已从会话中移除的)实例可以使用此方法重新关联到会话中:

user1 = User(name="user1")
user2 = User(name="user2")
session.add(user1)
session.add(user2)

session.commit()  # write changes to the database

要一次将一系列项目添加到会话中,请使用 Session.add_all():

session.add_all([item1, item2, item3])

Session.add() 操作 级联save-update 级联。有关更多详细信息,请参阅 级联 部分。### 删除

Session.delete() 方法将实例放入会话的待删除对象列表中:

# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)

# commit (or flush)
session.commit()

Session.delete() 标记对象以进行删除,这将导致针对每个受影响的主键发出 DELETE 语句。在待刷新的删除之前,被“删除”标记的对象存在于 Session.deleted 集合中。DELETE 后,它们从 Session 中删除,该会话在事务提交后变为永久。

Session.delete() 操作有各种与其他对象和集合的关系相关的重要行为。有关此操作的详细信息,请参阅 级联 部分,但总的规则是:

  • 与通过 relationship() 指令与被删除对象相关的映射对象对应的行默认情况下不会被删除。如果这些对象有一个外键约束返回到被删除的行,这些列将被设置为 NULL。如果这些列是非空的,这将导致约束违规。

  • 要将相关对象的行的“SET NULL”更改为删除,请在 relationship() 上使用 delete 级联。

  • 当表中的行通过relationship.secondary 参数链接为“多对多”表时,当它们所指向的对象被删除时,这些行在所有情况下都会被删除。

  • 当相关对象包含指向要删除对象的外键约束,并且它们所属的相关集合目前未加载到内存中时,工作单元将发出 SELECT 语句以获取所有相关行,以便它们的主键值可用于发出这些相关行的 UPDATE 或 DELETE 语句。这样,即使在 Core ForeignKeyConstraint 对象上配置了此功能,ORM 也会在没有进一步指示的情况下执行 ON DELETE CASCADE 的功能。

  • relationship.passive_deletes 参数可用于调整此行为并更自然地依赖于“ON DELETE CASCADE”;当设置为 True 时,此 SELECT 操作将不再发生,但是仍然存在的行仍将受到显式的 SET NULL 或 DELETE 影响。将 relationship.passive_deletes 设置为字符串"all"将禁用所有相关对象的更新/删除。

  • 当标记为删除的对象发生 DELETE 时,对象不会自动从引用它的集合或对象引用中删除。当Session过期时,这些集合可能会被重新加载,以便对象不再存在。然而,最好的做法是,不要对这些对象使用Session.delete(),而是应该从其集合中删除对象,然后使用 delete-orphan 来确保它在集合删除的次要影响下被删除。有关此的示例,请参阅删除说明 - 从集合和标量关系中删除对象 部分。

另请参阅

delete - 描述了“删除级联”,当主对象被删除时,标记相关对象以删除。

delete-orphan - 描述了“删除孤立对象级联”,当它们与其主对象解除关联时,将标记相关对象以删除。

删除注释 - 从集合和标量关系中删除引用的对象 - 关于Session.delete()的重要背景,涉及到在内存中刷新关系。### 刷新

Session使用其默认配置时,刷新步骤几乎总是透明完成的。具体来说,在因Query或 2.0 风格的Session.execute()调用而发出任何单个 SQL 语句之前,以及在Session.commit()调用中在事务提交之前,都会发生刷新。当使用Session.begin_nested()时,也会在发出 SAVEPOINT 之前发生刷新。

可以通过调用Session.flush()方法在任何时候强制进行Session刷新:

session.flush()

在某些方法的范围内自动发生的刷新称为自动刷新。自动刷新被定义为一种可配置的自动刷新调用,该调用发生在包括以下方法的开头:

  • 当针对 ORM 启用的 SQL 构造(例如引用 ORM 实体和/或 ORM 映射属性的select()对象)使用Session.execute()和其他执行 SQL 的方法时

  • 当调用Query以将 SQL 发送到数据库时

  • 在查询数据库之前的Session.merge()方法的过程中

  • 当对象被刷新

  • 当针对未加载对象属性进行 ORM 延迟加载操作时。

还有一些刷新发生的无条件点; 这些点位于关键的事务边界内,包括:

  • Session.commit()方法的过程中

  • 当调用Session.begin_nested()

  • 当使用Session.prepare() 2PC 方法时。

通过构造一个传递 Session.autoflush 参数为 FalseSessionsessionmaker,可以禁用 autoflush 行为,如前述条目列表所示:

Session = sessionmaker(autoflush=False)

此外,可以在使用 Session 期间通过使用 Session.no_autoflush 上下文管理器临时禁用自动刷新(autoflush):

with mysession.no_autoflush:
    mysession.add(some_object)
    mysession.flush()

再次强调: 当调用事务方法,如 Session.commit()Session.begin_nested() 时,无论任何“autoflush”设置,当 Session 仍有待处理的更改时,刷新过程 总是发生

由于 Session 仅在 DBAPI 事务上下文中调用数据库的 SQL,所有“flush”操作本身仅发生在数据库事务内部(取决于数据库事务的 隔离级别),前提是 DBAPI 不处于 驱动程序级别的自动提交 模式。这意味着假设数据库连接在其事务设置中提供了 原子性,如果 flush 内的任何单个 DML 语句失败,则整个操作将被回滚。

当 flush 中发生故障时,为了继续使用相同的 Session,需要在 flush 失败后显式调用 Session.rollback(),即使底层事务已经被回滚(即使数据库驱动程序在技术上处于驱动程序级别的自动提交模式)。这样做是为了始终保持所谓的“子事务”的整体嵌套模式。FAQ 部分 “This Session’s transaction has been rolled back due to a previous exception during flush.” (或类似内容) 中包含对此行为的更详细描述。

另请参阅

“此会话的事务已因刷新期间的先前异常而回滚。”(或类似内容) - 进一步解释为何在刷新失败时必须调用 Session.rollback()。 ### 通过主键获取

由于 Session 利用了一个身份映射,通过主键引用当前内存中的对象,因此 Session.get() 方法被提供为一种通过主键定位对象的方法,首先在当前身份映射中查找,然后查询数据库以获取不存在的值。例如,要定位主键标识为 (5, )User 实体:

my_user = session.get(User, 5)

Session.get() 也包括用于复合主键值的调用形式,可以作为元组或字典传递,以及允许特定加载器和执行选项的其他参数。有关完整参数列表,请参阅 Session.get()

另请参阅

Session.get() ### 过期 / 刷新

使用 Session 时经常遇到的一个重要考虑因素是处理从数据库加载的对象上存在的状态,以使其与当前事务的状态保持同步。SQLAlchemy ORM 基于身份映射的概念,这意味着当对象从 SQL 查询中“加载”时,将维护一个对应于特定数据库标识的唯一 Python 对象实例。这意味着如果我们发出两个单独的查询,每个查询都针对同一行,并获得一个映射对象,则两个查询将返回相同的 Python 对象:

>>> u1 = session.scalars(select(User).where(User.id == 5)).one()
>>> u2 = session.scalars(select(User).where(User.id == 5)).one()
>>> u1 is u2
True

接下来,当 ORM 从查询中获取行时,它将跳过已经加载的对象的属性填充。这里的设计假设是假设一个完全隔离的事务,然后在事务不完全隔离的程度上,应用程序可以根据需要从数据库事务中刷新对象。 此 FAQ 条目 在更详细地讨论了这个概念。

当 ORM 映射对象加载到内存中时,有三种常见方法可以使用当前事务中的新数据刷新其内容:

  • expire() 方法 - Session.expire() 方法将擦除对象的选定或全部属性的内容,以便在下次访问时从数据库加载它们,例如使用 延迟加载 模式:

    session.expire(u1)
    u1.some_attribute  # <-- lazy loads from the transaction
    
  • refresh() 方法 - 相关联的是 Session.refresh() 方法,它做的事情与 Session.expire() 方法相同,但还会立即发出一个或多个 SQL 查询,以实际刷新对象的内容:

    session.refresh(u1)  # <-- emits a SQL query
    u1.some_attribute  # <-- is refreshed from the transaction
    
  • populate_existing() 方法或执行选项 - 这现在是一个在 Populate Existing 中记录的执行选项;在传统形式中,它位于 Query 对象上,作为 Query.populate_existing() 方法。无论以哪种形式,此操作都表示从查询返回的对象应无条件地从数据库中重新填充:

    u2 = session.scalars(
        select(User).where(User.id == 5).execution_options(populate_existing=True)
    ).one()
    

关于刷新 / 过期概念的进一步讨论,请参阅 Refreshing / Expiring。

参见

刷新 / 过期

我正在使用我的 Session 重新加载数据,但它没有看到我在其他地方提交的更改

使用任意 WHERE 子句的 UPDATE 和 DELETE

SQLAlchemy 2.0 包括增强的功能,用于发出几种类型的启用 ORM 的 INSERT、UPDATE 和 DELETE 语句。有关文档,请参阅 ORM-Enabled INSERT, UPDATE, and DELETE statements。

参见

启用 ORM 的 INSERT、UPDATE 和 DELETE 语句

使用自定义 WHERE 条件的 ORM UPDATE 和 DELETE

自动开始

Session 对象具有一种称为 autobegin 的行为。这表示,一旦使用 Session 执行了任何工作,无论是涉及修改 Session 内部状态的工作还是涉及需要数据库连接的操作,Session 将在内部将自身视为处于“事务”状态。

当首次构建 Session 时,不存在事务状态。 当调用诸如 Session.add()Session.execute() 这样的方法时,或者类似地执行 Query 以返回结果(最终使用 Session.execute()),或者如果在 持久化 对象上修改属性时,将自动开始事务状态。

可以通过访问 Session.in_transaction() 方法来检查事务状态,该方法返回 TrueFalse,指示“自动开始”步骤是否已执行。 虽然通常不需要,但 Session.get_transaction() 方法将返回表示此事务状态的实际 SessionTransaction 对象。

Session 的事务状态也可以通过显式调用 Session.begin() 方法来启动。 当调用此方法时,Session 无条件地置于“事务性”状态。 Session.begin() 可以像在 框架化一个 begin / commit / rollback 块 中描述的那样用作上下文管理器。

禁用自动开始以防止隐式事务

可以使用 Session.autobegin 参数将“自动开始”行为设置为 False 以禁用该行为。通过使用此参数,Session 将要求显式调用 Session.begin() 方法。在构造后以及调用 Session.rollback()Session.commit()Session.close() 方法后,Session 不会隐式开始任何新事务,并且如果在首次调用 Session.begin() 之前尝试使用 Session,则会引发错误:

with Session(engine, autobegin=False) as session:
    session.begin()  # <-- required, else InvalidRequestError raised on next call

    session.add(User(name="u1"))
    session.commit()

    session.begin()  # <-- required, else InvalidRequestError raised on next call

    u1 = session.scalar(select(User).filter_by(name="u1"))

新版本 2.0 中新增了 Session.autobegin,允许禁用“自动开始”行为。 ### 提交

Session.commit() 用于提交当前事务。在核心上,这表示它会对所有当前具有进行中事务的数据库连接发出 COMMIT;从 DBAPI 的角度来看,这意味着会在每个 DBAPI 连接上调用 connection.commit() DBAPI 方法。

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

Session.commit() 操作在发出相关数据库连接的 COMMIT 前无条件发出 Session.flush()。如果未检测到挂起的更改,则不会向数据库发出 SQL。此行为不可配置,并且不受 Session.autoflush 参数的影响。

在此之后,假设Session绑定到一个Engine,那么Session.commit()将会提交实际的数据库事务,如果有的话。提交之后,与该事务相关联的Connection对象将被关闭,导致其底层的 DBAPI 连接被释放回与Session绑定的Engine相关联的连接池中。

对于绑定到多个引擎的Session(例如在分区策略中描述的),对于每个正在进行“逻辑”提交的Engine / Connection,相同的提交步骤将继续进行。这些数据库事务在未启用两阶段特性的情况下是不协调的。

还有其他的连接交互模式可用,通过直接将Session绑定到一个Connection;在这种情况下,假定存在一个外部管理的事务,并且在这种情况下不会自动发出真正的 COMMIT;请参阅将会话加入外部事务(例如用于测试套件)部分了解此模式的背景。

最后,在关闭事务时,Session中的所有对象都将被过期。这样,当实例下次被访问时,无论是通过属性访问还是通过它们出现在 SELECT 的结果中,它们都会接收到最新的状态。此行为可以通过Session.expire_on_commit标志来控制,当此行为不希望时,可以将其设置为False

另请参见

自动开始 ### 回滚

Session.rollback() 方法用于回滚当前事务(如果有的话)。当没有事务存在时,该方法会悄然通过。

使用默认配置的会话,在通过自动开始或显式调用Session.begin()方法开始事务后,会话的回滚后状态如下:

  • 数据库事务被回滚。对于绑定到单个EngineSession,这意味着对当前正在使用的最多一个Connection进行 ROLLBACK。对于绑定到多个Engine对象的Session对象,将对所有检出的Connection对象发出 ROLLBACK。
  • 数据库连接被释放。这遵循与提交中注意到的相同的与连接相关的行为,其中从Engine对象获取的Connection对象被关闭,导致 DBAPI 连接被释放到Engine中的连接池。如果开始新的事务,新连接将从Engine中检出。
  • 对于直接绑定到ConnectionSession(如加入外部事务的会话(例如用于测试套件)中描述的),此Connection上的回滚行为将遵循由Session.join_transaction_mode参数指定的行为,这可能涉及回滚保存点或发出真正的 ROLLBACK。
  • 在事务生命周期内将初始处于 pending 状态的对象从被添加到Session中的情况下,将被清除,对应于它们的 INSERT 语句被回滚。它们属性的状态保持不变。
  • 在事务生命周期内标记为 deleted 的对象将被提升回 persistent 状态,对应于它们的 DELETE 语句被回滚。请注意,如果这些对象在事务中首先是 pending,那么该操作将优先执行。
  • 所有未被清除的对象都完全过期 - 这与Session.expire_on_commit设置无关。

在理解了这种状态之后,Session可以在发生回滚后安全地继续使用。

从版本 1.4 开始更改:Session对象现在具有延迟“begin”行为,如 autobegin 中所述。如果没有开始事务,则Session.commit()Session.rollback()等方法将无效。在 1.4 之前不会观察到此行为,因为在非自动提交模式下,事务总是隐式存在。

Session.flush()失败时,通常是由于主键、外键或“非空”约束违反等原因,会自动发出 ROLLBACK(目前不可能在部分失败后继续刷新)。然而,此时Session进入一种称为“不活动”的状态,调用应用程序必须始终显式调用Session.rollback()方法,以便Session可以恢复到可用状态(也可以简单地关闭和丢弃)。有关进一步讨论,请参阅“由于刷新期间的先前异常,此会话的事务已被回滚。”(或类似)的常见问题解答。

参见

自动开始 ### 关闭

Session.close() 方法会调用 Session.expunge_all(),从会话中移除所有 ORM 映射的对象,并释放任何与其绑定的 Engine 对象的事务/连接资源。当连接返回到连接池时,事务状态也会回滚。

默认情况下,当 Session 关闭时,它实际上处于创建时的原始状态,可以再次使用。从这个意义上说,Session.close() 方法更像是一个“重置”到干净状态,而不是像一个“关闭数据库”的方法。在这种操作模式下,方法 Session.reset()Session.close() 的别名,并且行为相同。

通过将参数 Session.close_resets_only 设置为 False,可以更改 Session.close() 的默认行为,表示在调用方法 Session.close() 后不能重新使用 Session。在这种操作模式下,当 Session.close_resets_only 设置为 True 时,方法 Session.reset() 将允许会话的多次“重置”,行为类似于当 Session.close_resets_only 设置为 True 时的 Session.close()

版本 2.0.22 中新增。

建议通过在结尾处调用 Session.close() 来限制 Session 的范围,特别是如果没有使用 Session.commit()Session.rollback() 方法。Session 可以作为上下文管理器使用,以确保调用 Session.close()

with Session(engine) as session:
    result = session.execute(select(User))

# closes session automatically

在版本 1.4 中做了更改:Session 对象具有延迟“开始”行为,如 autobegin 中所述。在调用 Session.close() 方法后不再立即开始新的事务。

到此为止,许多用户已经对会话有了问题。本节介绍了使用 Session 时所面临的最基本问题的迷你 FAQ(请注意,我们还有一个 真正的 FAQ)。

何时创建 sessionmaker

只需一次,在应用程序的全局范围的某个地方。它应被视为应用程序配置的一部分。如果您的应用程序在一个包中有三个 .py 文件,您可以将 sessionmaker 行放在 __init__.py 文件中;从那时起,您的其他模块会说“from mypackage import Session”。这样,其他所有人只需使用 Session(),并且该会话的配置由该中心点控制。

如果您的应用程序启动,进行导入操作,但不知道将要连接到哪个数据库,您可以稍后在“类”级别将 Session 绑定到引擎,使用 sessionmaker.configure()

在本节中的示例中,我们经常会在实际调用 Session 的行的正上方显示 sessionmaker 被创建。但那只是为了举例说明!实际上,sessionmaker 会在模块级别的某个地方。对 Session 进行实例化的调用将放置在应用程序中开始数据库会话的地方。

我什么时候构建一个 Session,什么时候提交它,什么时候关闭它?

一个 Session 通常在可能预期到数据库访问的逻辑操作开始时构建。

每当 Session 用于与数据库通信时,它会在开始通信时立即启动数据库事务。此事务将持续进行,直到 Session 被回滚、提交或关闭。如果再次使用 Session,则会开始新的事务,接着之前的事务结束;由此可知,Session 可以跨多个事务具有生命周期,但一次只能进行一个。我们将这两个概念称为 事务范围会话范围

通常很容易确定何时开始和结束 Session 的范围,尽管可能存在多种应用程序架构,可能会引入具有挑战性的情况。

一些示例场景包括:

  • Web 应用程序。在这种情况下,最好利用所使用的 Web 框架提供的 SQLAlchemy 集成。或者,基本模式是在 Web 请求开始时创建一个Session,在执行 POST、PUT 或 DELETE 的 Web 请求结束时调用 Session.commit() 方法,然后在 Web 请求结束时关闭会话。通常还应该设置 Session.expire_on_commit 为 False,以便来自视图层的对象在事务已经提交后不需要发出新的 SQL 查询来刷新对象。

  • 一个后台守护进程会生成子进程,可能会想要为每个子进程创建一个Session,在该子进程处理的“作业”生命周期内使用该Session,然后在作业完成时将其销毁。

  • 对于命令行脚本,应用程序会创建一个在程序开始工作时建立的单一全局 Session,并在程序完成任务时立即提交它。

  • 对于基于 GUI 接口的应用程序,Session 的范围可能最好在用户生成的事件范围内,比如按钮点击。或者,范围可能与明确的用户交互相对应,比如用户“打开”一系列记录,然后“保存”它们。

一般来说,应用程序应该在处理特定数据的函数外部管理会话的生命周期。这是一种基本的关注点分离,使得特定于数据的操作不受其访问和操作数据的上下文的影响。

例如,不要这样做

### this is the **wrong way to do it** ###

class ThingOne:
    def go(self):
        session = Session()
        try:
            session.execute(update(FooBar).values(x=5))
            session.commit()
        except:
            session.rollback()
            raise

class ThingTwo:
    def go(self):
        session = Session()
        try:
            session.execute(update(Widget).values(q=18))
            session.commit()
        except:
            session.rollback()
            raise

def run_my_program():
    ThingOne().go()
    ThingTwo().go()

将会话(通常也是事务)的生命周期分离和外部化。下面的示例说明了这样做的方式,并且另外使用了 Python 上下文管理器(即 with: 关键字)来自动管理 Session 及其事务的范围:

### this is a **better** (but not the only) way to do it ###

class ThingOne:
    def go(self, session):
        session.execute(update(FooBar).values(x=5))

class ThingTwo:
    def go(self, session):
        session.execute(update(Widget).values(q=18))

def run_my_program():
    with Session() as session:
        with session.begin():
            ThingOne().go(session)
            ThingTwo().go(session)

自 1.4 版更改:Session 可以作为上下文管理器使用,无需使用外部辅助函数。

会话是缓存吗?

嗯……不是。它有点像缓存,因为它实现了 identity map 模式,并将对象存储为主键键控制的对象。但是,它不执行任何类型的查询缓存。这意味着,如果你说session.scalars(select(Foo).filter_by(name='bar')),即使Foo(name='bar')就在那里,在 identity map 中,会话也不知道。它必须向数据库发出 SQL,获取行,然后当它看到行中的主键时,然后它才能查看本地 identity map,并查看对象是否已存在。只有当你说query.get({some primary key})时,Session才不需要发出查询。

另外,默认情况下,会话(Session)使用弱引用来存储对象实例。这也违背了使用会话作为缓存的初衷。

Session不是设计为所有人都可以向其查询作为“注册表”的全局对象。这更像是第二级缓存的工作。SQLAlchemy 提供了使用dogpile.cache实现第二级缓存的模式,通过 Dogpile Caching 示例。

如何获取某个对象的Session

使用Session.object_session()类方法,该方法可用于Session

session = Session.object_session(someobject)

较新的运行时检查 API 系统也可以使用:

from sqlalchemy import inspect

session = inspect(someobject).session

会话(Session)是线程安全的吗?AsyncSession 在并发任务中安全吗?

Session是一个可变的、有状态的对象,表示一个单一的数据库事务。因此,Session的实例不能在并发线程或 asyncio 任务之间共享,除非进行仔细的同步Session旨在以非并发方式使用,即,特定的Session实例应在同一时间只在一个线程或任务中使用。

当使用 SQLAlchemy 的 asyncio 扩展中的AsyncSession对象时,该对象只是Session的一个薄代理,同样的规则适用;它是一个未同步的、可变的、有状态的对象,因此不安全在多个 asyncio 任务中同时使用单个AsyncSession实例。

SessionAsyncSession的一个实例代表一个单一的逻辑数据库事务,每次只引用一个特定EngineAsyncEngine的单个Connection(请注意,这些对象都支持同时绑定到多个引擎,但在这种情况下,在事务范围内仍然只有一个连接)。

事务中的数据库连接也是一个有状态的对象,应该以非并发、顺序的方式进行操作。命令按照序列在连接上发出,并由数据库服务器按照发出的确切顺序处理。当Session发出命令并接收结果时,Session本身正在经历与此连接上的命令和数据状态相一致的内部状态更改;这些状态包括事务是否已启动、提交或回滚,正在使用的 SAVEPOINT(如果有),以及将数据库行的状态与本地 ORM 映射的对象同步的细粒度同步。

在为并发设计数据库应用程序时,适当的模型是每个并发任务/线程都使用自己的数据库事务。这就是为什么在讨论数据库并发问题时,使用的标准术语是多个并发事务。在传统的关系型数据库管理系统中,没有类似于同时接收和处理多个命令的单个数据库事务的模拟。

因此,SQLAlchemy 的SessionAsyncSession的并发模型是每个线程一个 Session,每个任务一个 AsyncSession。一个使用多个线程或多个 asyncio 任务的应用(例如使用像asyncio.gather()这样的 API)将希望确保每个线程有其自己的Session,每个 asyncio 任务有其自己的AsyncSession

确保此使用的最佳方法是在线程或任务内部的顶级 Python 函数中本地使用标准上下文管理器模式,这将确保SessionAsyncSession的生命周期在局部范围内维护。

对于那些受益于具有“全局”Session的应用,其中无法将Session对象传递给需要它的特定函数和方法的情况,scoped_session方法可以提供一个“线程本地”的Session对象;请参阅上下文/线程本地会话一节了解背景。在 asyncio 环境中,async_scoped_session对象是scoped_session的 asyncio 模拟,但是更难配置,因为它需要一个自定义的“上下文”函数。

Session 做什么?

在最一般的意义上,Session建立了与数据库的所有交流,并代表了在其生命周期内加载或关联的所有对象的“持有区”。它提供了 SELECT 和其他查询的接口,这些查询将返回和修改 ORM 映射的对象。ORM 对象本身被维护在Session内部,存储在一个称为 identity map 的结构中 - 这是一种维护每个对象唯一副本的数据结构,其中“唯一”意味着“只有一个具有特定主键的对象”。

Session 在最常见的使用模式下,以大部分无状态的形式开始。一旦发出查询或使用其他对象进行持久化,它会从与 Session 关联的 Engine 请求连接资源,然后在该连接上建立事务。这个事务会一直保持到 Session 被指示提交或回滚事务。事务结束时,与 Engine 关联的连接资源会被释放到引擎管理的连接池中。然后,一个新的事务会开始,使用一个新的连接。

Session 维护的 ORM 对象被装饰,这样每当 Python 程序中的属性或集合被修改时,就会生成一个变更事件,由 Session 记录下来。每当即将查询数据库或即将提交事务时,Session 首先会将内存中的所有待定更改刷新到数据库中。这被称为工作单元模式。

当使用 Session 时,将其维护的 ORM 映射对象视为代理对象以访问数据库行,这些对象局限于由 Session 持有的事务。为了保持对象的状态与实际数据库中的内容一致,有多种事件会导致对象重新访问数据库以保持同步。可以将对象“分离”(detach)出 Session,并继续使用它们,尽管这种做法有其注意事项。通常情况下,你应该重新将分离的对象与另一个 Session 关联,以便在需要时恢复其正常的数据库状态表示任务。

使用会话的基础知识

这里介绍了最基本的 Session 使用模式。

开启和关闭会话

Session可以独立构造,也可以使用sessionmaker类构造。通常,它被作为连通性的源传递给单个Engine。典型的用法可能如下所示:

from sqlalchemy import create_engine
from sqlalchemy.orm import Session

# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# create session and add objects
with Session(engine) as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()

在上面的示例中,Session与特定数据库 URL 关联的Engine一起实例化。然后,它在 Python 上下文管理器(即with:语句)中使用,因此它在块结束时会自动关闭;这相当于调用Session.close()方法。

Session.commit()的调用是可选的,只有当我们与Session一起完成的工作包括要持久化到数据库的新数据时才需要。如果我们只发出 SELECT 调用并且不需要写入任何更改,则对Session.commit()的调用将是不必要的。

注意

注意,在调用Session.commit()之后,无论是明确调用还是使用上下文管理器,与Session关联的所有对象都将被过期,这意味着它们的内容将被擦除以在下一个事务中重新加载。如果这些对象是分离的,则在重新关联到新的Session之前,它们将无法正常工作,除非使用Session.expire_on_commit参数来禁用此行为。更多细节请参阅 Committing 部分。

我们还可以将 Session.commit() 调用和事务的整体“框架”封装在一个上下文管理器中,对于那些需要将数据提交到数据库的情况。所谓的“框架”是指如果所有操作都成功,则会调用 Session.commit() 方法,但如果引发任何异常,则会立即调用 Session.rollback() 方法,以便立即回滚事务,然后向外传播异常。在 Python 中,这主要是使用 try: / except: / else: 块来表达的,例如:

# verbose version of what a context manager will do
with Session(engine) as session:
    session.begin()
    try:
        session.add(some_object)
        session.add(some_other_object)
    except:
        session.rollback()
        raise
    else:
        session.commit()

通过使用 Session.begin() 方法返回的 SessionTransaction 对象,可以更简洁地实现上面示例中的长序列操作,该对象为相同序列操作提供了一个上下文管理器接口:

# create session and add objects
with Session(engine) as session:
    with session.begin():
        session.add(some_object)
        session.add(some_other_object)
    # inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

更简洁地说,这两个上下文可以合并:

# create session and add objects
with Session(engine) as session, session.begin():
    session.add(some_object)
    session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

使用 sessionmaker

sessionmaker 的目的是为具有固定配置的 Session 对象提供一个工厂。由于典型的情况是应用程序将在模块范围内拥有一个 Engine 对象,sessionmaker 可以为针对此引擎的 Session 对象提供一个工厂:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources, typically in module scope
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()
# closes the session

sessionmaker 类似于 Engine,作为一个模块级别的工厂,用于创建函数级别的会话 / 连接。因此,它也有自己的 sessionmaker.begin() 方法,类似于 Engine.begin(),它返回一个 Session 对象,并且也维护一个 begin/commit/rollback 块:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() and include begin()/commit()/rollback()
# at once
with Session.begin() as session:
    session.add(some_object)
    session.add(some_other_object)
# commits the transaction, closes the session

在上面的情况下,当上述 with: 块结束时,Session 的事务将被提交,并且 Session 将被关闭。

当您编写应用程序时,sessionmaker工厂应该与由create_engine()创建的Engine对象作用域相同,通常是在模块级或全局级。由于这些对象都是工厂,因此它们可以被任意数量的函数和线程同时使用。

另请参阅

sessionmaker

Session

查询

查询的主要手段是利用select()构造来创建一个Select对象,然后使用诸如Session.execute()Session.scalars()等方法执行该对象以返回结果。结果以Result对象的形式返回,包括诸如ScalarResult等子变体。

SQLAlchemy ORM 查询的完整指南可在 ORM 查询指南中找到。以下是一些简要示例:

from sqlalchemy import select
from sqlalchemy.orm import Session

with Session(engine) as session:
    # query for ``User`` objects
    statement = select(User).filter_by(name="ed")

    # list of ``User`` objects
    user_obj = session.scalars(statement).all()

    # query for individual columns
    statement = select(User.name, User.fullname)

    # list of Row objects
    rows = session.execute(statement).all()

从 2.0 版本开始更改:“2.0”风格查询现在是标准的。请参阅 2.0 迁移 - ORM 使用了解从 1.x 系列迁移的注意事项。

另请参阅

ORM 查询指南 ### 添加新项目或现有项目

使用Session.add()将实例放入会话中。对于瞬态(即全新的)实例,这将导致在下一次刷新时对这些实例进行插入操作。对于持久(即由此会话加载的)实例,它们已经存在,不需要添加。可以使用此方法将分离(即已从会话中删除的)实例重新关联到会话中:

user1 = User(name="user1")
user2 = User(name="user2")
session.add(user1)
session.add(user2)

session.commit()  # write changes to the database

要一次向会话中添加一系列项目,请使用Session.add_all()

session.add_all([item1, item2, item3])

Session.add() 操作沿着 save-update 级联进行。有关详细信息,请参阅 Cascades 部分。### 删除

Session.delete() 方法将一个实例放入会话的待删除对象列表中:

# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)

# commit (or flush)
session.commit()

Session.delete()标记一个对象为删除状态,这将导致为每个受影响的主键发出一个 DELETE 语句。在挂起的删除被刷新之前,由“delete”标记的对象存在于 Session.deleted 集合中。删除之后,它们将从 Session 中删除,在事务提交后,这变得永久。

有关Session.delete() 操作的各种重要行为,特别是关于如何处理与其他对象和集合的关系的行为。有关此工作原理的更多信息,请参阅 Cascades 部分,但总的来说规则是:

  • 通过relationship()指令关联到已删除对象的映射对象行默认不会被删除。如果这些对象具有指回被删除行的外键约束,这些列将设置为 NULL。如果列是非空的,这将导致约束违规。

  • 要将相关对象行的“SET NULL”更改为删除,请在 relationship() 上使用 delete 级联。

  • 当链接为“many-to-many”表的表中的行通过relationship.secondary参数链接时,当它们所引用的对象被删除时,它们在所有情况下都将被删除。

  • 当相关对象包含指回正在删除的对象的外键约束,并且它们所属的相关集合当前未加载到内存中时,工作单元将发出一个 SELECT 来获取所有相关行,以便它们的主键值可以用于发出 UPDATE 或 DELETE 语句以处理这些相关行。通过这种方式,ORM 即使在 Core ForeignKeyConstraint 对象上配置了 ON DELETE CASCADE,也会执行这个功能,而不需要进一步的指示。

  • relationship.passive_deletes参数可用于调整此行为,并更自然地依赖于“ON DELETE CASCADE”;当设置为 True 时,此 SELECT 操作将不再发生,但仍然会对本地存在的行进行显式的 SET NULL 或 DELETE。将relationship.passive_deletes设置为字符串"all"将禁用所有相关对象的更新/删除。

  • 当标记为删除的对象发生删除时,该对象不会自动从引用它的集合或对象引用中移除。当Session过期时,这些集合可能会再次加载,以便对象不再存在。然而,最好的做法是,不要对这些对象使用Session.delete(),而是应该从其集合中移除对象,然后使用 delete-orphan 以便作为该集合移除的次要效果而被删除。请参阅 Notes on Delete - Deleting Objects Referenced from Collections and Scalar Relationships 部分以获取示例。

另请参见

delete - 描述了“删除级联”,当主对象被删除时,会标记相关对象以进行删除。

delete-orphan - 描述了“删除孤儿级联”,当相关对象与其主对象解除关联时,会标记这些相关对象以进行删除。

Notes on Delete - Deleting Objects Referenced from Collections and Scalar Relationships - 关于Session.delete()的重要背景,涉及在内存中刷新关系。### 刷新

Session 使用其默认配置时,刷新步骤几乎总是透明进行的。具体来说,在由 Query 或 2.0 风格 的 Session.execute() 调用导致发出任何单个 SQL 语句之前,以及在 Session.commit() 调用中在提交事务之前,都会发生刷新。当使用 Session.begin_nested() 时,还会在发出 SAVEPOINT 之前发生刷新。

可以随时通过调用 Session.flush() 方法来强制执行 Session 的刷新:

session.flush()

在某些方法的范围内自动发生的刷新称为自动刷新。自动刷新定义为在包括以下方法的开头发生的可配置的自动刷新调用:

  • Session.execute() 和其他执行 SQL 的方法,在针对启用了 ORM 的 SQL 构造时使用,比如指向 ORM 实体和/或 ORM 映射属性的 select() 对象

  • 当调用 Query 发送 SQL 到数据库时

  • 在查询数据库之前 Session.merge() 方法中

  • 当对象被刷新

  • 当针对未加载的对象属性执行 ORM 延迟加载 操作时。

还有一些无条件发生刷新的点;这些点位于关键事务边界内,包括:

  • Session.commit() 方法的过程中

  • 调用 Session.begin_nested()

  • 当使用 Session.prepare() 2PC 方法时。

对于上述项目列表所应用的自动刷新行为,可以通过构建一个传递了Session.autoflush参数为FalseSessionsessionmaker来禁用它:

Session = sessionmaker(autoflush=False)

此外,可以在使用Session时使用Session.no_autoflush上下文管理器临时禁用自动刷新:

with mysession.no_autoflush:
    mysession.add(some_object)
    mysession.flush()

重申一下: 当调用事务方法(如Session.commit()Session.begin_nested())时,刷新过程总是发生,无论任何“自动刷新”设置如何,当Session仍有待处理的更改时。

由于Session只在 DBAPI 事务的上下文中调用 SQL 到数据库,所有“flush”操作本身只发生在数据库事务内(受数据库事务的隔离级别的影响),前提是 DBAPI 不处于驱动级别自动提交模式。这意味着假设数据库连接在其事务设置中提供了原子性,如果刷新内部的任何个别 DML 语句失败,整个操作将被回滚。

当刷新过程中发生故障时,为了继续使用相同的Session,在刷新失败后需要显式调用Session.rollback(),即使底层事务已经回滚了(即使数据库驱动程序在技术上处于驱动程序级别的自动提交模式)。这样做是为了始终保持所谓“子事务”的整体嵌套模式。 FAQ 部分“由于刷新期间的先前异常,此会话的事务已回滚。”(或类似)中包含了对此行为的更详细描述。

另请参阅

“由于刷新期间发生的先前异常,此会话的事务已回滚。”(或类似) - 关于在刷新失败时必须调用Session.rollback()的更多背景信息。 ### 按主键获取

由于Session利用了一个标识映射,该映射通过主键引用当前内存中的对象,因此Session.get()方法被提供用于通过主键定位对象,首先在当前标识映射内查找,然后在数据库中查询不存在的值。例如,要定位主键标识为(5, )User实体:

my_user = session.get(User, 5)

Session.get() 还包括对复合主键值的调用形式,可以作为元组或字典传递,以及允许特定加载程序和执行选项的其他参数。请参阅Session.get()获取完整的参数列表。

另请参阅

Session.get() ### 过期/刷新

在使用Session时经常会出现的一个重要考虑因素是处理从数据库加载的对象上存在的状态,以保持它们与事务的当前状态同步。SQLAlchemy ORM 基于标识映射的概念,因此当从 SQL 查询中“加载”对象时,将维护一个对应于特定数据库标识的唯一 Python 对象实例。这意味着如果我们发出两个单独的查询,每个查询都针对同一行,并返回一个映射对象,则这两个查询将返回相同的 Python 对象:

>>> u1 = session.scalars(select(User).where(User.id == 5)).one()
>>> u2 = session.scalars(select(User).where(User.id == 5)).one()
>>> u1 is u2
True

由此可见,当 ORM 从查询中返回行时,将跳过已加载的对象的属性填充。这里的设计假设是假定一个完全隔离的事务,然后在事务不完全隔离的程度上,应用程序可以根据需要从数据库事务中刷新对象。在我正在重新加载我的 Session 中的数据,但它没有看到我在其他地方提交的更改的 FAQ 条目中更详细地讨论了这个概念。

当 ORM 映射对象加载到内存中时,有三种常规方法可以使用当前事务中的新数据刷新其内容:

  • expire() 方法 - Session.expire() 方法将擦除对象的选定或全部属性的内容,以便在下次访问它们时从数据库加载,例如使用惰性加载模式:

    session.expire(u1)
    u1.some_attribute  # <-- lazy loads from the transaction
    
  • refresh() 方法 - 与之密切相关的是Session.refresh() 方法,它执行Session.expire() 方法执行的所有操作,但还立即发出一个或多个 SQL 查询来实际刷新对象的内容:

    session.refresh(u1)  # <-- emits a SQL query
    u1.some_attribute  # <-- is refreshed from the transaction
    
  • populate_existing() 方法或执行选项 - 现在这是一个在填充现有中记录的执行选项;在传统形式中,它在Query对象上作为Query.populate_existing()方法找到。无论采取哪种形式,此操作表示应从数据库中的内容无条件地重新填充从查询返回的对象:

    u2 = session.scalars(
        select(User).where(User.id == 5).execution_options(populate_existing=True)
    ).one()
    

关于刷新/过期概念的进一步讨论可在刷新/过期找到。

另请参阅

刷新/过期

我正在使用我的会话重新加载数据,但它没有看到我在其他地方提交的更改

使用任意 WHERE 子句的 UPDATE 和 DELETE

SQLAlchemy 2.0 包括增强功能,可发出几种类型的 ORM 启用的 INSERT、UPDATE 和 DELETE 语句。有关文档,请参阅 ORM-启用的 INSERT、UPDATE 和 DELETE 语句。

另请参阅

ORM-启用的 INSERT、UPDATE 和 DELETE 语句

带有自定义 WHERE 条件的 ORM UPDATE 和 DELETE

自动开始

Session 对象具有称为autobegin的行为。这表示Session一旦执行了与Session相关的任何工作,无论涉及对Session的内部状态进行修改还是需要数据库连接的操作,它都会在内部将自身视为处于“事务”状态。

当首次构造Session时,不存在事务状态。当调用方法如Session.add()Session.execute()时,或类似地执行用于返回结果的Query(最终使用Session.execute()),或者在持久化对象上修改属性时,事务状态将自动开始。

检查事务状态可以通过访问Session.in_transaction()方法来实现,该方法返回TrueFalse,指示“自动开始”步骤是否已经执行。虽然通常不需要,但Session.get_transaction()方法将返回表示此事务状态的实际SessionTransaction对象。

也可以通过调用Session.begin()方法显式启动Session的事务状态。调用此方法时,Session无条件地被置于“事务性”状态。Session.begin()可以像描述的那样用作上下文管理器,详见构建开始 / 提交 / 回滚块的框架。

禁用 Autobegin 以防止隐式事务

可以通过将Session.autobegin参数设置为False来禁用“自动开始”行为。通过使用此参数,Session将要求显式调用Session.begin()方法。在构造之后以及在调用任何Session.rollback()Session.commit()Session.close()方法之后,如果尝试在没有首先调用Session.begin()的情况下使用Session,则不会隐式启动任何新事务,并且将引发错误:

with Session(engine, autobegin=False) as session:
    session.begin()  # <-- required, else InvalidRequestError raised on next call

    session.add(User(name="u1"))
    session.commit()

    session.begin()  # <-- required, else InvalidRequestError raised on next call

    u1 = session.scalar(select(User).filter_by(name="u1"))

新版本 2.0 中新增:添加Session.autobegin,允许禁用“自动开始”行为 ### 提交

Session.commit() 用于提交当前事务。本质上,这表示在所有当前具有正在进行的事务的数据库连接上发出COMMIT;从 DBAPI 的角度来看,这意味着在每个 DBAPI 连接上调用connection.commit() DBAPI 方法。

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

在发出相关数据库连接上的 COMMIT 之前,Session.commit() 操作无条件地发出Session.flush()。如果未检测到待处理的更改,则不会向数据库发出任何 SQL。此行为不可配置,并且不受Session.autoflush参数的影响。

在此之后,假设Session绑定到一个EngineSession.commit()将提交当前的数据库事务,如果已经启动。提交后,与该事务关联的Connection对象将关闭,导致其底层的 DBAPI 连接被释放回与Session绑定的Engine相关联的连接池。

对于绑定到多个引擎的Session(例如在分区策略中描述的方式),对正在提交的“逻辑”事务中涉及的每个Engine / Connection都将执行相同的 COMMIT 步骤。除非启用了两阶段功能,否则这些数据库事务之间不协调。

其他连接-交互模式也是可用的,通过将Session直接绑定到Connection;在这种情况下,假定存在外部管理的事务,并且在这种情况下不会自动发出真正的 COMMIT;有关此模式的背景,请参阅将 Session 加入外部事务(例如测试套件)部分。

最后,在事务关闭时,Session中的所有对象都会被过期。这样,当下次访问实例时,无论是通过属性访问还是通过它们出现在 SELECT 的结果中,它们都会接收到最新状态。这种行为可以由Session.expire_on_commit标志来控制,当此行为不可取时可以将其设置为False

另请参阅

自动开始 ### 回滚

Session.rollback()回滚当前事务(如果有)。当没有事务时,该方法会静默地通过。

默认配置的会话后回滚状态,即通过 autobegin 或显式调用Session.begin()方法开始事务后的状态如下:

  • 数据库事务被回滚。对于绑定到单个EngineSession,这意味着对当前正在使用的最多一个Connection进行回滚。对于绑定到多个Engine对象的Session对象,将对所有被检出的Connection对象进行回滚。
  • 数据库连接被释放。这遵循了提交中注意到的与连接相关的相同行为,即从Engine对象获取的Connection对象被关闭,导致 DBAPI 连接被释放到 Engine 中的连接池中。如果有新的事务开始,会从Engine中检出新的连接。
  • 对于直接绑定到ConnectionSession,如将会话加入外部事务(例如测试套件)中描述的,此Connection上的回滚行为将遵循由Session.join_transaction_mode参数指定的行为,这可能涉及回滚保存点或发出真正的 ROLLBACK。
  • 在事务的生命周期内,当对象被添加到Session时最初处于挂起状态的对象将被清除,对应其 INSERT 语句被回滚的情况。它们属性的状态保持不变。
  • 在事务的生命周期内被标记为已删除的对象将被提升回持久状态,对应其 DELETE 语句被回滚的情况。请注意,如果这些对象在事务中首先处于挂起状态,则该操作优先级较高。
  • 所有未清除的对象都将完全过期 - 这与Session.expire_on_commit设置无关。

在了解了这种状态后,Session在回滚发生后可以安全地继续使用。

从版本 1.4 开始更改:Session对象现在具有延迟“begin”行为,如自动开始中所述。如果未开始任何事务,则Session.commit()Session.rollback()等方法将不起作用。在 1.4 之前,不会观察到这种行为,因为在非自动提交模式下,事务总是隐式存在的。

Session.flush()失败时,通常是由于主键、外键或“非空”约束违规等原因,会自动发出 ROLLBACK(当前不可能在部分失败后继续 flush)。但是,在此时,Session进入一种称为“不活跃”的状态,调用应用程序必须始终显式调用Session.rollback()方法,以便Session可以回到可用状态(也可以简单地关闭并丢弃)。有关进一步讨论,请参阅 FAQ 条目“此会话的事务由于刷新时的先前异常而被回滚。”(或类似)。

另请参阅

自动开始 ### 结束

Session.close() 方法会发出一个Session.expunge_all(),它将从会话中删除所有 ORM 映射的对象,并且释放与其绑定的Engine对象的所有事务/连接资源。当连接返回到连接池时,事务状态也会被回滚。

默认情况下,当Session关闭时,它基本上处于最初构建时的原始状态,并且可以再次使用。从这个意义上说,Session.close() 方法更像是“重置”回到清洁状态,而不太像是“关闭数据库”的方法。在这种操作模式下,方法Session.reset()Session.close()的别名,并且行为相同。

Session.close() 的默认行为可以通过将参数Session.close_resets_only设置为False来更改,这表明在调用方法Session.close()后,Session不能被重用。在这种操作模式下,当Session.close_resets_only设置为True时,方法Session.reset()将允许会话的多次“重置”,表现得像Session.close()一样。

2.0.22 版本中的新增功能。

建议在结束时通过调用 Session.close() 来限制 Session 的范围,特别是如果未使用 Session.commit()Session.rollback() 方法。 Session 可以用作上下文管理器,以确保调用 Session.close()

with Session(engine) as session:
    result = session.execute(select(User))

# closes session automatically

自 1.4 版更改:Session 对象具有延迟“begin”行为,如 autobegin 中所述。在调用 Session.close() 方法后,不再立即开始新事务。### 开启和关闭会话

Session 可以自行构建,也可以使用 sessionmaker 类。通常,它最初会作为连接性的源传递单个 Engine。典型的用法可能如下:

from sqlalchemy import create_engine
from sqlalchemy.orm import Session

# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# create session and add objects
with Session(engine) as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()

上面,Session 是通过与特定数据库 URL 关联的 Engine 实例化的。然后在 Python 上下文管理器中使用(即 with: 语句),以便在块结束时自动关闭;这相当于调用 Session.close() 方法。

调用 Session.commit() 是可选的,只有在我们与 Session 一起完成的工作包括要持久化到数据库的新数据时才需要。如果我们只是发出 SELECT 调用并且不需要写入任何更改,则调用 Session.commit() 将是不必要的。

注意,在调用Session.commit()之后,无论是显式调用还是使用上下文管理器,与Session相关联的所有对象都将被过期,这意味着它们的内容将被擦除以在下一个事务中重新加载。如果这些对象被分离,它们将无法正常工作,直到与新的Session重新关联,除非使用Session.expire_on_commit参数来禁用此行为。更多详细信息请参见提交部分。

制定开始/提交/回滚块的框架

我们还可以将Session.commit()调用和事务的整体“框架”封装在上下文管理器中,以用于那些将数据提交到数据库的情况。所谓“框架”是指如果所有操作成功,则会调用Session.commit()方法,但如果引发任何异常,则会立即调用Session.rollback()方法,以便立即回滚事务,然后将异常传播出去。在 Python 中,这主要是通过try:/except:/else:块来表达的,例如:

# verbose version of what a context manager will do
with Session(engine) as session:
    session.begin()
    try:
        session.add(some_object)
        session.add(some_other_object)
    except:
        session.rollback()
        raise
    else:
        session.commit()

上面示例的长形操作序列可以通过使用Session.begin()方法返回的SessionTransaction对象来更简洁地实现,该对象为相同操作序列提供了上下文管理器接口:

# create session and add objects
with Session(engine) as session:
    with session.begin():
        session.add(some_object)
        session.add(some_other_object)
    # inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

更简洁地,这两个上下文可以结合使用:

# create session and add objects
with Session(engine) as session, session.begin():
    session.add(some_object)
    session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

使用 sessionmaker

sessionmaker的目的是提供一个固定配置的Session对象工厂。由于典型的应用程序在模块范围内会有一个Engine对象,sessionmaker可以为与此引擎对应的Session对象提供一个工厂:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources, typically in module scope
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()
# closes the session

sessionmakerEngine 类似,是用于函数级会话/连接的模块级工厂。因此,它还有自己的 sessionmaker.begin() 方法,类似于 Engine.begin(),它返回一个 Session 对象,并且还维护一个开始/提交/回滚块:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() and include begin()/commit()/rollback()
# at once
with Session.begin() as session:
    session.add(some_object)
    session.add(some_other_object)
# commits the transaction, closes the session

在上述情况下,当上述 with: 块结束时,Session 将提交其事务,并且 Session 将关闭。

在编写应用程序时,sessionmaker 工厂应该与 create_engine() 创建的 Engine 对象保持相同的作用域,通常在模块级或全局级别。由于这些对象都是工厂,因此它们可以被任意数量的函数和线程同时使用。

另请参阅

sessionmaker

Session

查询

查询的主要方式是利用 select() 构造创建一个 Select 对象,然后使用诸如 Session.execute()Session.scalars() 等方法执行以返回结果。结果然后以 Result 对象的形式返回,包括诸如 ScalarResult 等子变体。

SQLAlchemy ORM 查询指南提供了完整的 SQLAlchemy ORM 查询指南。以下是一些简要示例:

from sqlalchemy import select
from sqlalchemy.orm import Session

with Session(engine) as session:
    # query for ``User`` objects
    statement = select(User).filter_by(name="ed")

    # list of ``User`` objects
    user_obj = session.scalars(statement).all()

    # query for individual columns
    statement = select(User.name, User.fullname)

    # list of Row objects
    rows = session.execute(statement).all()

从版本 2.0 开始更改:“2.0”样式查询现在是标准的。请参阅 2.0 迁移 - ORM 使用 查看从 1.x 系列迁移的注意事项。

另请参阅

ORM 查询指南

添加新的或现有的项目

Session.add() 用于将实例放入会话中。对于临时(即全新)的实例,这将在下一次刷新时导致对这些实例进行 INSERT。对于持久(即由此会话加载的)实例,它们已经存在,不需要添加。分离(即已从会话中移除的)实例可以使用此方法重新关联到会话中:

user1 = User(name="user1")
user2 = User(name="user2")
session.add(user1)
session.add(user2)

session.commit()  # write changes to the database

要一次性向会话中添加项目列表,请使用Session.add_all()

session.add_all([item1, item2, item3])

Session.add() 操作级联执行save-update级联。更多详情请参阅 Cascades 部分。

删除

Session.delete() 方法将一个实例放入会话的待删除对象列表中:

# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)

# commit (or flush)
session.commit()

Session.delete() 标记一个对象为删除状态,这将导致为每个受影响的主键发出 DELETE 语句。在挂起的删除被刷新之前,“delete”标记的对象存在于Session.deleted集合中。删除后,它们将从Session中移除,在事务提交后,这将变得永久。

Session.delete()操作相关的各种重要行为,尤其是与其他对象和集合的关系如何处理的行为。有关此操作的更多信息,请参见 Cascades 部分,但通常规则如下:

  • 通过relationship()指令与已删除对象相关联的映射对象对应的行默认不会被删除。如果这些对象对被删除的行有一个外键约束,则这些列将设置为 NULL。如果这些列是非空的,这将导致约束违反。

  • 要将“SET NULL”更改为相关对象行的 DELETE,请使用relationship() 上的删除级联。

  • 作为“多对多”表链接的表中的行,通过relationship.secondary参数,所有情况下都会被删除,当它们引用的对象被删除时。

  • 当相关对象包含返回到正在删除的对象的外键约束,并且它们所属的相关集合当前未加载到内存中时,工作单元将发出 SELECT 来获取所有相关行,以便它们的主键值可以用于发出 UPDATE 或 DELETE 语句在这些相关行上。通过这种方式,ORM 将在没有进一步指示的情况下执行 ON DELETE CASCADE 的功能,即使这在 Core ForeignKeyConstraint对象上进行了配置。

  • relationship.passive_deletes参数可用于调整此行为,并更自然地依赖于“ON DELETE CASCADE”;当设置为 True 时,此 SELECT 操作将不再发生,但是仍将对本地存在的行进行显式的 SET NULL 或 DELETE。将relationship.passive_deletes设置为字符串"all"将禁用所有相关对象的更新/删除。

  • 当标记为删除的对象发生删除时,并不会自动从引用它的集合或对象引用中删除该对象。当Session过期时,这些集合可能会再次加载,以便对象不再存在。但是,最好不要对这些对象使用Session.delete(),而是应该从其集合中移除对象,然后使用 delete-orphan,以使其作为该集合移除的副作用而被删除。请参见部分关于删除 - 从集合和标量关系中删除引用的对象以获取示例。

参见

delete - 描述了“级联删除”,当主对象被删除时,会标记相关对象为删除。

delete-orphan - 描述了“孤立删除级联”,当它们从其主对象中取消关联时,会标记相关对象为删除。

删除说明 - 删除从集合和标量关系引用的对象 - 有关Session.delete()的重要背景,涉及在内存中刷新关系。

刷新

Session以其默认配置使用时,刷新步骤几乎总是在透明地完成。具体来说,在由Query或 2.0 风格的Session.execute()调用引发任何单个 SQL 语句的情况下,以及在提交事务之前的Session.commit()调用之前,都会发生刷新。它还在使用Session.begin_nested()时发出 SAVEPOINT 前发生。

可以通过调用Session.flush()方法随时强制执行Session刷新:

session.flush()

在某些方法的范围内自动发生的刷新称为自动刷新。自动刷新被定义为在以下方法开始时发生的可配置的自动刷新调用:

  • 当针对启用 ORM 的 SQL 构造执行方法(如针对 ORM 实体和/或 ORM 映射属性的select()对象)使用Session.execute()和其他执行 SQL 的方法时

  • 当调用Query来将 SQL 发送到数据库时

  • 在查询数据库之前的Session.merge()方法内

  • 当对象被刷新

  • 当针对未加载对象属性进行 ORM 惰性加载 操作时。

也有一些刷新会无条件发生;这些点位于关键事务边界内,包括:

  • Session.commit()方法的过程中

  • 当调用Session.begin_nested()

  • 当使用Session.prepare() 2PC 方法时。

作为前面项目的一部分应用的自动刷新行为可以通过构造一个传递SessionsessionmakerSession.autoflush参数为False来禁用:

Session = sessionmaker(autoflush=False)

另外,自动刷新可以在使用Session的流程中暂时禁用,使用Session.no_autoflush上下文管理器:

with mysession.no_autoflush:
    mysession.add(some_object)
    mysession.flush()

重申一下: 当调用事务方法(例如Session.commit()Session.begin_nested())时,刷新过程总是发生,而不管任何“自动刷新”设置如何,当Session还有未处理的待处理更改时。

由于Session仅在 DBAPI 事务的上下文中调用数据库的 SQL,所有“刷新”操作本身仅发生在数据库事务内(取决于数据库事务的隔离级别),前提是 DBAPI 不处于驱动程序级别的自动提交模式。这意味着假设数据库连接在其事务设置中提供了原子性,如果刷新中的任何单个 DML 语句失败,整个操作都将被回滚。

在刷新过程中发生故障时,为了继续使用相同的Session,即使底层事务已经回滚(即使数据库驱动程序在技术上处于驱动程序级别的自动提交模式),也需要在刷新失败后显式调用Session.rollback(),这样可以保持所谓的“子事务”的整体嵌套模式的一致性。“由于刷新期间发生的先前异常,此会话的事务已被回滚。”(或类似)FAQ 部分包含了对此行为的更详细描述。

另请参阅

“由于在刷新期间发生的先前异常,此会话的事务已回滚。”(或类似) - 关于在刷新失败时为什么必须调用 Session.rollback() 的更多背景信息。

按主键获取

由于 Session 使用的是一个 标识映射,它通过主键引用当前内存中的对象,因此 Session.get() 方法被提供作为一种通过主键定位对象的方法,首先查找当前标识映射,然后查询数据库以获取不存在的值。例如,要定位具有主键标识 (5, )User 实体:

my_user = session.get(User, 5)

Session.get() 还包括用于复合主键值的调用形式,可以作为元组或字典传递,以及允许特定加载器和执行选项的附加参数。参见 Session.get() 以获取完整的参数列表。

另请参阅

Session.get()

过期 / 刷新

在使用 Session 时经常会遇到的一个重要考虑因素是处理从数据库加载的对象上存在的状态,以保持它们与事务的当前状态同步。SQLAlchemy ORM 是基于一个 标识映射 的概念,即当从 SQL 查询中“加载”对象时,将维护一个与特定数据库标识相对应的唯一 Python 对象实例。这意味着如果我们发出两个单独的查询,每个查询都针对同一行,并获得一个映射对象,则这两个查询将返回相同的 Python 对象:

>>> u1 = session.scalars(select(User).where(User.id == 5)).one()
>>> u2 = session.scalars(select(User).where(User.id == 5)).one()
>>> u1 is u2
True

由此可见,当 ORM 从查询中获取行时,它将跳过已加载对象的属性的填充。这里的设计假设是假设一个完全隔离的事务,然后根据事务的隔离程度,应用程序可以根据需要采取步骤从数据库事务中刷新对象。我正在使用我的会话重新加载数据,但它看不到我在其他地方提交的更改的 FAQ 条目更详细地讨论了这个概念。

当将 ORM 映射对象加载到内存中时,有三种常见方法可以使用当前事务的新数据刷新其内容:

  • expire() 方法 - Session.expire() 方法将擦除对象的选定或所有属性的内容,以便在下次访问时从数据库加载它们,例如使用延迟加载模式:

    session.expire(u1)
    u1.some_attribute  # <-- lazy loads from the transaction
    
  • refresh() 方法 - 与之密切相关的是Session.refresh()方法,它执行Session.expire()方法的所有操作,但还会立即发出一个或多个 SQL 查询以实际刷新对象的内容:

    session.refresh(u1)  # <-- emits a SQL query
    u1.some_attribute  # <-- is refreshed from the transaction
    
  • populate_existing() 方法或执行选项 - 这现在是一个在 Populate Existing 文档中记录的执行选项;在传统形式中,它在Query对象上作为Query.populate_existing()方法找到。无论以哪种形式,此操作都表示从查询返回的对象应无条件地从数据库中重新填充:

    u2 = session.scalars(
        select(User).where(User.id == 5).execution_options(populate_existing=True)
    ).one()
    

有关刷新 / 过期概念的进一步讨论,请参阅刷新 / 过期。

另请参阅

刷新 / 过期

我正在使用我的 Session 重新加载数据,但它没有看到我在其他地方提交的更改

使用任意 WHERE 子句的 UPDATE 和 DELETE

SQLAlchemy 2.0 包括增强功能,用于发出几种 ORM 启用的 INSERT、UPDATE 和 DELETE 语句。请参阅 ORM-Enabled INSERT, UPDATE, and DELETE statements 文档。

另请参阅

ORM-Enabled INSERT, UPDATE, and DELETE statements

使用自定义 WHERE 条件的 ORM UPDATE 和 DELETE

自动开始

Session对象具有称为autobegin的行为。这表示当使用Session执行任何工作时,无论涉及修改Session的内部状态以进行对象状态更改,还是涉及需要数据库连接的操作,Session将在内部认为自己处于“事务”状态。

Session 第一次被构造时,没有事务状态存在。当调用诸如 Session.add()Session.execute() 这样的方法时,事务状态会自动开始,或者类似地,如果执行 Query 来返回结果(最终使用 Session.execute()),或者如果在 持久化 对象上修改属性。

可以通过访问 Session.in_transaction() 方法来检查事务状态,该方法返回 TrueFalse,指示“自动开始”步骤是否已执行。虽然通常不需要,但 Session.get_transaction() 方法将返回表示此事务状态的实际 SessionTransaction 对象。

Session 的事务状态也可以通过显式调用 Session.begin() 方法来启动。当调用此方法时,Session 无条件地处于“事务”状态。Session.begin() 可以像 框架化一个 begin / commit / rollback 块 中描述的那样用作上下文管理器。

禁用 Autobegin 以防止隐式事务

“自动开始”行为可以通过将Session.autobegin参数设置为False来禁用。通过使用此参数,Session将要求显式调用Session.begin()方法。在构造时,以及在调用任何Session.rollback()Session.commit()Session.close()方法之后,Session不会隐式开始任何新事务,并且如果尝试在未首先调用Session.begin()的情况下使用Session,将会引发错误:

with Session(engine, autobegin=False) as session:
    session.begin()  # <-- required, else InvalidRequestError raised on next call

    session.add(User(name="u1"))
    session.commit()

    session.begin()  # <-- required, else InvalidRequestError raised on next call

    u1 = session.scalar(select(User).filter_by(name="u1"))

新版本 2.0 中:添加了 Session.autobegin,允许禁用“自动开始”行为 #### 禁用自动开始以防止隐式事务

“自动开始”行为可以通过将Session.autobegin参数设置为False来禁用。通过使用此参数,Session将要求显式调用Session.begin()方法。在构造时,以及在调用任何Session.rollback()Session.commit()Session.close()方法之后,Session不会隐式开始任何新事务,并且如果尝试在未首先调用Session.begin()的情况下使用Session,将会引发错误:

with Session(engine, autobegin=False) as session:
    session.begin()  # <-- required, else InvalidRequestError raised on next call

    session.add(User(name="u1"))
    session.commit()

    session.begin()  # <-- required, else InvalidRequestError raised on next call

    u1 = session.scalar(select(User).filter_by(name="u1"))

新版本 2.0 中:添加了 Session.autobegin,允许禁用“自动开始”行为

提交

Session.commit() 用于提交当前事务。在其核心,这表示对所有当前具有正在进行事务的数据库连接发出COMMIT;从 DBAPI 的角度来看,这意味着在每个 DBAPI 连接上调用 connection.commit() DBAPI 方法。

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

Session.commit() 操作在发出相关数据库连接的 COMMIT 之前无条件地发出 Session.flush()。如果未检测到待定更改,则不会向数据库发出 SQL。此行为不可配置,并且不受 Session.autoflush 参数的影响。

在此之后,假设Session绑定到一个Engine,则Session.commit()将 COMMIT 实际的数据库事务,如果已启动。提交后,与该事务关联的Connection对象将关闭,导致其底层的 DBAPI 连接被释放回与Session绑定的Engine相关联的连接池。

对于绑定到多个引擎(例如在分区策略中描述的那样)的Session,相同的 COMMIT 步骤将为每个在“逻辑”事务中使用的Engine / Connection 进行。除非启用了两阶段功能,否则这些数据库事务之间是不协调的。

还有其他的连接交互模式,可以直接将Session绑定到Connection上;在这种情况下,假定存在外部管理的事务,并且在这种情况下不会自动发出真正的 COMMIT;有关此模式的背景信息,请参见加入外部事务的会话(例如测试套件)一节。

最后,Session内的所有对象在事务关闭时都会被过期。这样,在下次访问实例时,无论是通过属性访问还是通过它们存在于 SELECT 结果中,它们都会接收到最新的状态。此行为可以通过Session.expire_on_commit标志来控制,当不希望这种行为时,可以将其设置为False

另请参阅

自动开始

回滚

Session.rollback()回滚当前事务(如果有)。当没有事务时,该方法会静默地通过。

默认配置的会话(session)后,会话的事务回滚状态,其后续是通过自动开始或显式调用Session.begin()方法开始事务后的情况如下:

  • 数据库事务将被回滚。对于绑定到单个EngineSession,这意味着针对当前正在使用的最多一个Connection发出 ROLLBACK。对于绑定到多个Engine对象的Session对象,针对所有被检出的Connection对象发出 ROLLBACK。
  • 数据库连接将被释放。这遵循了提交中指出的与连接相关的相同行为,其中从Engine对象获取的Connection对象将被关闭,导致 DBAPI 连接被释放到Engine中的连接池中。如果有新的事务开始,则会从Engine中检出新连接。
  • 对于直接绑定到ConnectionSession,如将会话加入外部事务(比如测试套件),此Connection上的回滚行为将遵循Session.join_transaction_mode参数指定的行为,这可能涉及回滚保存点或发出真正的 ROLLBACK。
  • 在事务生命周期内将挂起状态的对象从添加到Session中时的状态是被移除的,对应于其 INSERT 语句的回滚。它们的属性状态保持不变。
  • 已删除对象在事务生命周期内被重新提升到持久化状态,对应其 DELETE 语句被回滚。请注意,如果这些对象首先在事务内为挂起状态,那么该操作将优先进行。
  • 所有未清除的对象都将完全过期 - 这与Session.expire_on_commit设置无关。

了解了该状态后,Session可以在回滚发生后安全地继续使用。

从 1.4 版本开始更改:Session对象现在具有延迟“开始”行为,如 autobegin 中所述。如果未开始任何事务,则Session.commit()Session.rollback()等方法不会产生任何效果。在 1.4 版本之前不会观察到此行为,因为在非自动提交模式下,事务始终会隐式存在。

Session.flush()失败时,通常是由于主键、外键或“非空”约束违反等原因,将自动发出 ROLLBACK(目前不可能在部分失败后继续 flush)。但是,此时Session处于一种称为“不活跃”的状态,并且调用应用程序必须始终显式调用Session.rollback()方法,以使Session可以恢复到可用状态(也可以简单地关闭和丢弃)。有关详细讨论,请参阅“由于刷新期间发生先前异常,此会话的事务已被回滚。”(或类似)的常见问题解答条目。

另请参阅

自动开始

结束

Session.close() 方法会调用 Session.expunge_all() 方法,该方法会将会话中的所有 ORM 映射对象移除,并且释放与其绑定的 Engine 对象的所有事务/连接资源。当连接返回到连接池时,事务状态也会回滚。

默认情况下,当 Session 被关闭时,它实际上处于最初构造时的原始状态,并且可以再次使用。从这个意义上讲,Session.close() 方法更像是一个“重置”回到干净状态,而不太像一个“数据库关闭”方法。在这种操作模式下,方法 Session.reset()Session.close() 的别名,并且以相同的方式运行。

Session.close() 的默认行为可以通过设置参数 Session.close_resets_onlyFalse 来更改,表示在调用方法 Session.close() 后,Session 不能被重复使用。在这种操作模式下,当 Session.close_resets_only 设置为 True 时,方法 Session.reset() 将允许多次“重置”会话,表现得像 Session.close()

新功能在版本 2.0.22 中添加。

强烈建议通过调用 Session.close() 在结束时限制 Session 的范围,特别是如果没有使用 Session.commit()Session.rollback() 方法。Session 可以作为上下文管理器使用,以确保调用 Session.close()

with Session(engine) as session:
    result = session.execute(select(User))

# closes session automatically

自 1.4 版更改:Session 对象具有延迟“开始”行为,如 autobegin 中所述,在调用 Session.close() 方法后不会立即开始新的事务。

Session 常见问题

到了这个时候,许多用户已经对会话有了疑问。本节介绍了使用 Session 时常见问题的迷你常见问题解答(请注意我们也有一个真实常见问题解答)。

我什么时候使用 sessionmaker

只需一次,在你应用程序的全局范围内的某个地方。它应该被视为应用程序配置的一部分。如果你的应用程序在一个包中有三个 .py 文件,你可以将 sessionmaker 行放在你的 __init__.py 文件中;从那时起,你的其他模块会说“from mypackage import Session”。这样,其他人只需使用 Session(),而该会话的配置由该中心点控制。

如果你的应用程序启动,进行导入,但不知道将连接到什么数据库,你可以稍后在“类”级别将 Session 绑定到引擎,使用 sessionmaker.configure()

在本节的示例中,我们经常会在实际调用Session的代码行的上方展示sessionmaker的创建。但那只是为了举例而已!在实际情况中,sessionmaker可能会在模块级别的某处。然后,实例化Session的调用将放置在应用程序开始数据库交谈的地方。

我什么时候构建一个Session,什么时候提交它,什么时候关闭它?

通常在预期可能需要数据库访问的逻辑操作的开始处构造Session

每当使用Session与数据库通信时,Session都会在开始通信时启动数据库事务。此事务将持续进行,直到Session被回滚、提交或关闭。如果再次使用它,则Session将开始一个新的事务,继续上一个事务结束的位置;因此,Session能够在多个事务中具有生命周期,尽管一次只能有一个事务。我们将这两个概念称为事务范围会话范围

通常情况下,确定Session的范围的最佳时机并不是很困难,尽管可能存在各种各样的应用架构,这可能会引入具有挑战性的情况。

一些示例场景包括:

  • Web 应用程序。在这种情况下,最好利用正在使用的 Web 框架提供的 SQLAlchemy 集成。或者,基本模式是在 Web 请求开始时创建一个Session,在执行 POST、PUT 或 DELETE 的 Web 请求结束时调用Session.commit() 方法,然后在 Web 请求结束时关闭会话。通常也是一个好主意将Session.expire_on_commit设置为 False,这样视图层中来自Session的对象的后续访问就不需要发出新的 SQL 查询来刷新对象,如果事务已经提交。

  • 一个产生子进程的后台守护程序将希望为每个子进程创建一个本地的Session,在该子进程处理的“作业”的生命周期内使用该Session,然后在作业完成时将其关闭。

  • 对于命令行脚本,应用程序将创建一个单一的全局Session,当程序开始工作时建立,完成任务时立即提交。

  • 对于 GUI 接口驱动的应用程序,Session 的范围最好在用户生成的事件范围内,比如按钮点击。或者,范围可能对应于显式用户交互,比如用户“打开”一系列记录,然后“保存”它们。

作为一般规则,应用程序应该在外部管理会话的生命周期,而不是在处理特定数据的函数中。这是一种基本的关注点分离,使得特定于数据的操作与它们访问和操作数据的上下文无关。

例如不要这样做

### this is the **wrong way to do it** ###

class ThingOne:
    def go(self):
        session = Session()
        try:
            session.execute(update(FooBar).values(x=5))
            session.commit()
        except:
            session.rollback()
            raise

class ThingTwo:
    def go(self):
        session = Session()
        try:
            session.execute(update(Widget).values(q=18))
            session.commit()
        except:
            session.rollback()
            raise

def run_my_program():
    ThingOne().go()
    ThingTwo().go()

将会话的生命周期(通常是事务)分开并置于外部。下面的示例说明了这可能是什么样子,并且另外利用了 Python 上下文管理器(即with:关键字)来自动管理Session及其事务的范围:

### this is a **better** (but not the only) way to do it ###

class ThingOne:
    def go(self, session):
        session.execute(update(FooBar).values(x=5))

class ThingTwo:
    def go(self, session):
        session.execute(update(Widget).values(q=18))

def run_my_program():
    with Session() as session:
        with session.begin():
            ThingOne().go(session)
            ThingTwo().go(session)

从版本 1.4 开始:Session 可以作为上下文管理器使用,而无需使用外部帮助函数。

会话是缓存吗?

是的……不。它在某种程度上被用作缓存,因为它实现了标识映射模式,并将对象按其主键键入存储。但它不执行任何查询缓存。这意味着,即使 Foo(name='bar') 就在那里,位于标识映射中,如果你说 session.scalars(select(Foo).filter_by(name='bar')),会话也不知道那个。它必须向数据库发出 SQL,获取行,然后当它看到行中的主键时,然后它才能查看本地标识映射,并看到对象已经在那里。只有当你说 query.get({some primary key}) 时,Session 才不需要发出查询。

此外,默认情况下,会话使用弱引用存储对象实例。这也使得将会话用作缓存失去了意义。

Session 并不是设计成每个人都可以作为“注册表”查阅的全局对象。这更像是第二级缓存的工作。SQLAlchemy 提供了使用 dogpile.cache 实现第二级缓存的模式,通过 Dogpile Caching 示例。

如何获取某个对象的 Session

使用 Session.object_session() 类方法,该方法可用于 Session

session = Session.object_session(someobject)

较新的 Runtime Inspection API 系统也可以使用:

from sqlalchemy import inspect

session = inspect(someobject).session

会话是否线程安全?AsyncSession 在并发任务中是否安全共享?

Session 是一个可变的,有状态的对象,表示一个单个的数据库事务。因此,Session 的实例不能在并发线程或 asyncio 任务之间共享,除非进行了仔细的同步Session 应该以非并发的方式使用,也就是说,一个特定的 Session 实例一次只应该在一个线程或任务中使用。

当使用 SQLAlchemy 的 asyncio 扩展中的AsyncSession对象时,此对象仅是Session的一个薄代理,并且相同的规则适用;它是一个非同步、可变、有状态的对象,因此安全在多个 asyncio 任务中同时使用单个AsyncSession实例。

一个SessionAsyncSession的实例表示一个单一的逻辑数据库事务,每次仅引用一个特定的EngineAsyncEngine的单一Connection(注意,这些对象都支持同时绑定到多个引擎,但在这种情况下,在事务范围内仍然每个引擎只有一个连接在起作用)。

事务中的数据库连接也是一个有状态对象,旨在以非并发、顺序方式进行操作。命令按顺序在连接上发出,数据库服务器以发出的确切顺序处理它们。当Session发出针对此连接的命令并接收结果时,Session本身正在通过与此连接上存在的命令和数据状态相一致的内部状态变化进行过渡;这些状态包括是否开始、提交或回滚事务,如果有的话,正在起作用的 SAVEPOINT,以及将数据库行的状态与本地 ORM 映射对象的细粒度同步。

在设计并发数据库应用程序时,适当的模型是每个并发任务/线程都使用自己的数据库事务。这就是为什么在讨论数据库并发问题时,使用的标准术语是多个并发事务。在传统的关系数据库管理系统中,没有单个数据库事务的类似物,它可以同时接收和处理多个命令。

因此,SQLAlchemy 的 SessionAsyncSession 的并发模型是 每个线程一个 Session,每个任务一个 AsyncSession。一个使用多个线程或多个 asyncio 任务的应用程序,例如在使用 asyncio.gather() 这样的 API 时,会希望确保每个线程都有自己的Session,每个 asyncio 任务都有自己的 AsyncSession

确保此用法的最佳方法是在位于线程或任务内的顶级 Python 函数中本地使用 标准上下文管理器模式,这将确保SessionAsyncSession的生命周期在本地范围内维护。

对于那些受益于拥有“全局”Session的应用程序,在不方便将Session对象传递给需要它的特定函数和方法的情况下,scoped_session方法可以提供一个“线程本地”的Session对象;详见 上下文/线程本地会话 部分。在 asyncio 上下文中,async_scoped_session对象是 scoped_session 的 asyncio 类比,然而更难配置,因为它需要一个自定义的“上下文”函数。

在什么时候我会使用sessionmaker

只需要一次,在应用程序的全局范围内的某处。它应被视为应用程序配置的一部分。如果你的应用程序在一个包中有三个 .py 文件,你可以将sessionmaker行放在你的 __init__.py 文件中;从那时起,你的其他模块会说“from mypackage import Session”。这样,其他人只需使用Session(),而该会话的配置由该中心点控制。

如果你的应用程序启动,导入了模块,但不知道要连接到哪个数据库,你可以在之后将Session在“类”级别绑定到引擎上,使用sessionmaker.configure()

在本节的示例中,我们经常会在实际调用Session的代码行的上方创建sessionmaker。但那只是为了举例说明!实际上,sessionmaker应该在模块级别的某个地方。然后,在应用程序中开始数据库会话的地方会放置对Session的实例化调用。

我什么时候构建Session,什么时候提交它,什么时候关闭它?

Session通常是在可能需要访问数据库的逻辑操作开始时构建的。

每当使用Session与数据库通信时,会立即开始一个数据库事务。该事务会一直持续到Session被回滚、提交或关闭。如果再次使用了Session,则会开始一个新的事务,这意味着Session可以跨越多个事务的生命周期,尽管一次只能有一个事务。我们将这两个概念称为事务范围会话范围

通常很容易确定开始和结束Session范围的最佳时机,尽管可能出现各种各样的应用程序架构,会引入具有挑战性的情况。

一些示例场景包括:

  • Web 应用程序。在这种情况下,最好利用所使用的 Web 框架提供的 SQLAlchemy 集成。或者,基本模式是在 Web 请求开始时创建一个Session,在进行 POST、PUT 或 DELETE 的 Web 请求结束时调用 Session.commit() 方法,然后在 Web 请求结束时关闭该会话。通常还建议将 Session.expire_on_commit 设置为 False,以便在视图层中访问来自 Session 的对象后,如果事务已经提交,就不需要发出新的 SQL 查询来刷新对象。

  • 一个后台守护进程,会派生出子进程,希望为每个子进程创建一个Session,在子进程处理“任务”期间使用该Session,然后在任务完成时将其销毁。

  • 对于命令行脚本,应用程序将创建一个单一的全局Session,当程序开始工作时建立,并在程序完成任务时立即提交。

  • 对于 GUI 接口驱动的应用程序,Session 的范围最好在用户生成的事件范围内,例如按钮按下。或者,范围可能对应于显式用户交互,例如用户“打开”一系列记录,然后“保存”它们。

通常情况下,应用程序应该将会话的生命周期 外部 管理到处理特定数据的函数之外。这是一种将数据特定操作与它们访问和操作数据的上下文隔离开来的基本分离原则。

例如,不要这样做

### this is the **wrong way to do it** ###

class ThingOne:
    def go(self):
        session = Session()
        try:
            session.execute(update(FooBar).values(x=5))
            session.commit()
        except:
            session.rollback()
            raise

class ThingTwo:
    def go(self):
        session = Session()
        try:
            session.execute(update(Widget).values(q=18))
            session.commit()
        except:
            session.rollback()
            raise

def run_my_program():
    ThingOne().go()
    ThingTwo().go()

保持会话(通常是事务)的生命周期分离和外部。下面的示例说明了这可能的外观,并且另外利用了 Python 上下文管理器(即 with: 关键字)来自动管理Session及其事务的范围:

### this is a **better** (but not the only) way to do it ###

class ThingOne:
    def go(self, session):
        session.execute(update(FooBar).values(x=5))

class ThingTwo:
    def go(self, session):
        session.execute(update(Widget).values(q=18))

def run_my_program():
    with Session() as session:
        with session.begin():
            ThingOne().go(session)
            ThingTwo().go(session)

从 1.4 版本开始更改:Session 可以作为上下文管理器使用,无需使用外部辅助函数。

会话是一个缓存吗?

不是的。在某种程度上它被用作缓存,因为它实现了身份映射模式,并将对象键入其主键。但是,它不会做任何类型的查询缓存。这意味着,如果你说 session.scalars(select(Foo).filter_by(name='bar')),即使 Foo(name='bar') 正好在那里,在身份映射中,会话也不知道。它必须向数据库发出 SQL,获取行,然后当它看到行中的主键时,然后它可以查看本地身份映射并查看对象是否已经存在。只有当你说 query.get({some primary key}) 时,Session 才不必发出查询。

另外,Session 默认使用弱引用存储对象实例。这也破坏了将 Session 用作缓存的目的。

Session 并不是设计成一个所有人都可以查询的全局对象作为“注册表”对象的。这更像是二级缓存的工作。SQLAlchemy 提供了使用 dogpile.cache 实现二级缓存的模式,通过 Dogpile Caching 示例。

如何获取某个对象的 Session

使用 Session.object_session() 类方法,该方法在 Session 上可用:

session = Session.object_session(someobject)

较新的 运行时检查 API 系统也可以使用:

from sqlalchemy import inspect

session = inspect(someobject).session

Session 线程安全吗?AsyncSession 在并发任务中安全吗?

Session 是一个可变的、有状态的对象,表示一个单一的数据库事务。因此,Session 的实例不能在并发线程或 asyncio 任务之间共享,除非进行仔细的同步Session 应该以非并发方式使用,也就是说,特定的 Session 实例一次只能在一个线程或任务中使用。

当使用 SQLAlchemy 的 asyncio 扩展中的AsyncSession对象时,此对象只是Session的一个薄代理,相同的规则适用;它是一个不同步的、可变的、有状态的对象,因此安全在多个 asyncio 任务中同时使用单个AsyncSession实例。

一个SessionAsyncSession实例代表一个单一的逻辑数据库事务,一次只引用一个特定EngineAsyncEngine的单一Connection,该对象绑定到其中(请注意,这些对象都支持同时绑定到多个引擎,但在这种情况下,在事务范围内仍然每个引擎只有一个连接在使用)。

事务中的数据库连接也是一个有状态的对象,旨在以非并发、顺序方式进行操作。命令按顺序在连接上发出,数据库服务器按照发出的顺序精确处理它们。当Session在此连接上发出命令并接收结果时,Session本身正在通过与此连接上存在的命令和数据状态一致的内部状态更改过渡;这些状态包括事务是否已开始、已提交或已回滚,是否存在任何 SAVEPOINT,以及个别数据库行的状态与本地 ORM 映射对象的状态之间的细粒度同步。

在为并发设计数据库应用程序时,适当的模型是每个并发任务/线程使用自己的数据库事务。这就是为什么在讨论数据库并发问题时,使用的标准术语是多个并发事务。在传统的 RDMS 中,没有类似于同时接收和处理多个命令的单个数据库事务的类比。

因此,SQLAlchemy 的 SessionAsyncSession 的并发模型是 每线程一个 Session,每任务一个 AsyncSession。使用多个线程或在 asyncio 中使用多个任务(例如使用 asyncio.gather() 这样的 API)的应用程序将希望确保每个线程都有其自己的 Session,每个 asyncio 任务都有其自己的 AsyncSession

确保此用法的最佳方式是在线程或任务内部的顶级 Python 函数中本地使用标准上下文管理器模式,这将确保 SessionAsyncSession 的生命周期在本地范围内保持不变。

对于从具有“全局” Session 中受益的应用程序,在不将 Session 对象传递给需要它的特定函数和方法的情况下,scoped_session 方法可以提供“线程本地” Session 对象;请参阅上下文/线程本地会话 部分了解背景。在 asyncio 上下文中,async_scoped_session 对象是 scoped_session 的 asyncio 版本,但更难配置,因为它需要自定义的“上下文”函数。

posted @ 2024-06-22 11:36  绝不原创的飞龙  阅读(49)  评论(0编辑  收藏  举报