前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SqlAlchemy 2.0 中文文档(二十四)

SqlAlchemy 2.0 中文文档(二十四)

作者头像
ApacheCN_飞龙
发布2024-06-26 14:54:03
1390
发布2024-06-26 14:54:03
举报
文章被收录于专栏:信数据得永生信数据得永生

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

附加持久化技术

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

将 SQL 插入/更新表达式嵌入到刷新中

此功能允许将数据库列的值设置为 SQL 表达式而不是文字值。这对于原子更新、调用存储过程等特别有用。您所要做的就是将表达式分配给属性:

代码语言:javascript
复制
class SomeClass(Base):
    __tablename__ = "some_table"

    # ...

    value = mapped_column(Integer)

someobject = session.get(SomeClass, 5)

# set 'value' attribute to a SQL expression adding one
someobject.value = SomeClass.value + 1

# issues "UPDATE some_table SET value=value+1"
session.commit()

这种技术对于 INSERT 和 UPDATE 语句都适用。在刷新/提交操作之后,上述someobjectvalue属性将会过期,因此在下次访问时,新生成的值将从数据库加载。

该功能还具有条件支持,可以与主键列一起使用。对于支持 RETURNING 的后端(包括 Oracle、SQL Server、MariaDB 10.5、SQLite 3.35),还可以将 SQL 表达式分配给主键列。这允许对 SQL 表达式进行评估,并允许在 INSERT 时修改主键值的服务器端触发器成功地由 ORM 作为对象的主键的一部分检索:

代码语言:javascript
复制
class Foo(Base):
    __tablename__ = "foo"
    pk = mapped_column(Integer, primary_key=True)
    bar = mapped_column(Integer)

e = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", echo=True)
Base.metadata.create_all(e)

session = Session(e)

foo = Foo(pk=sql.select(sql.func.coalesce(sql.func.max(Foo.pk) + 1, 1)))
session.add(foo)
session.commit()

在 PostgreSQL 上,上述Session将发出以下 INSERT:

代码语言:javascript
复制
INSERT  INTO  foo  (foopk,  bar)  VALUES
((SELECT  coalesce(max(foo.foopk)  +  %(max_1)s,  %(coalesce_2)s)  AS  coalesce_1
FROM  foo),  %(bar)s)  RETURNING  foo.foopk

新版本 1.3 中:现在可以在 ORM 刷新期间将 SQL 表达式传递给主键列;如果数据库支持 RETURNING,或者正在使用 pysqlite,ORM 将能够将服务器生成的值作为主键属性的值检索出来。 ## 在会话中使用 SQL 表达式

SQL 表达式和字符串可以通过Session在其事务上下文中执行。最简单的方法是使用Session.execute()方法,它以与EngineConnection相同的方式返回一个CursorResult

代码语言:javascript
复制
Session = sessionmaker(bind=engine)
session = Session()

# execute a string statement
result = session.execute(text("select * from table where id=:id"), {"id": 7})

# execute a SQL expression construct
result = session.execute(select(mytable).where(mytable.c.id == 7))

Session持有的当前Connection可通过Session.connection()方法访问:

代码语言:javascript
复制
connection = session.connection()

上述示例涉及绑定到单个EngineConnectionSession。要使用绑定到多个引擎或根本没有绑定(即依赖于绑定元数据)的Session执行语句,Session.execute()Session.connection()都接受一个绑定参数字典Session.execute.bind_arguments,其中可能包括“mapper”,该参数传递了一个映射类或Mapper实例,用于定位所需引擎的适当上下文:

代码语言:javascript
复制
Session = sessionmaker()
session = Session()

# need to specify mapper or class when executing
result = session.execute(
    text("select * from table where id=:id"),
    {"id": 7},
    bind_arguments={"mapper": MyMappedClass},
)

result = session.execute(
    select(mytable).where(mytable.c.id == 7), bind_arguments={"mapper": MyMappedClass}
)

connection = session.connection(MyMappedClass)

从 1.4 版本开始更改:Session.execute()中的mapperclause参数现在作为发送给Session.execute.bind_arguments参数的字典的一部分传递。之前的参数仍然被接受,但此用法已被弃用。 ## 强制在具有默认值的列上使用 NULL

ORM 将任何从未在对象上设置过的属性视为“默认”情况;该属性将在 INSERT 语句中被省略:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String(50), nullable=True)

obj = MyObject(id=1)
session.add(obj)
session.commit()  # INSERT with the 'data' column omitted; the database
# itself will persist this as the NULL value

在 INSERT 中省略一列意味着该列将设置为 NULL 值,除非该列设置了默认值,此时默认值将被保留。这既适用于纯 SQL 角度的服务器端默认值,也适用于 SQLAlchemy 在客户端和服务器端默认值下的插入行为:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String(50), nullable=True, server_default="default")

obj = MyObject(id=1)
session.add(obj)
session.commit()  # INSERT with the 'data' column omitted; the database
# itself will persist this as the value 'default'

然而,在 ORM 中,即使将 Python 值None明确地分配给对象,这与从未分配值一样处理:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String(50), nullable=True, server_default="default")

obj = MyObject(id=1, data=None)
session.add(obj)
session.commit()  # INSERT with the 'data' column explicitly set to None;
# the ORM still omits it from the statement and the
# database will still persist this as the value 'default'

上述操作将将服务器默认值"default"持久化到data列中,而不是 SQL NULL,即使传递了None;这是 ORM 的一个长期行为,许多应用程序将其视为一种假设。

那么如果我们想要实际将 NULL 放入这一列中,即使该列有默认值呢?有两种方法。一种是在每个实例级别上,我们使用null SQL 构造来分配属性:

代码语言:javascript
复制
from sqlalchemy import null

obj = MyObject(id=1, data=null())
session.add(obj)
session.commit()  # INSERT with the 'data' column explicitly set as null();
# the ORM uses this directly, bypassing all client-
# and server-side defaults, and the database will
# persist this as the NULL value

null SQL 构造总是直接在目标 INSERT 语句中转换为 SQL NULL 值。

如果我们希望能够使用 Python 值 None 并且尽管存在列默认值,也希望将其持久化为 NULL,则可以在 ORM 中使用一个 Core 级别的修改器 TypeEngine.evaluates_none() 进行配置,该修改器指示 ORM 应该将值 None 与任何其他值一样对待,并将其传递,而不是将其视为“缺失”的值而省略它:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(
        String(50).evaluates_none(),  # indicate that None should always be passed
        nullable=True,
        server_default="default",
    )

obj = MyObject(id=1, data=None)
session.add(obj)
session.commit()  # INSERT with the 'data' column explicitly set to None;
# the ORM uses this directly, bypassing all client-
# and server-side defaults, and the database will
# persist this as the NULL value
```## 获取服务器生成的默认值

如在服务器调用的 DDL-显式默认表达式和标记隐式生成的值、时间戳和触发列章节中介绍的,Core 支持数据库列的概念,即数据库自身在 INSERT 语句中生成一个值,以及在较少见的情况下,在 UPDATE 语句中生成一个值。ORM 功能支持此类列,以便在刷新时能够获取这些新生成的值。在服务器生成的主键列的情况下,这种行为是必需的,因为一旦对象被持久化,ORM 就必须知道对象的主键。

在绝大多数情况下,由数据库自动生成值的主键列是简单的整数列,数据库实现为所谓的“自增”列,或者从与列关联的序列中生成。SQLAlchemy Core 中的每个数据库方言都支持一种检索这些主键值的方法,这种方法通常是 Python DBAPI 本地的,并且一般情况下这个过程是自动的。关于这一点还有更多的文档资料在 `Column.autoincrement`。

对于不是主键列或不是简单自增整数列的服务器生成列,ORM 要求这些列用适当的 `server_default` 指令标记,以允许 ORM 检索此值。然而,并非所有方法都支持所有后端,因此必须小心使用适当的方法。要回答的两个问题是,1\. 这一列是否是主键,2\. 数据库是否支持 RETURNING 或等效方法,如“OUTPUT inserted”;这些是在调用 INSERT 或 UPDATE 语句时同时返回服务器生成的值的 SQL 短语。RETURNING 目前由 PostgreSQL、Oracle、MariaDB 10.5、SQLite 3.35 和 SQL Server 支持。

### 情况 1:非主键,支持 RETURNING 或等效方法

在这种情况下,列应标记为 `FetchedValue` 或具有显式的 `Column.server_default`。当执行 INSERT 语句时,ORM 将自动将这些列添加到 RETURNING 子句中,假设 `Mapper.eager_defaults` 参数设置为 `True`,或者如果将其默认设置为 `"auto"`,对于同时支持 RETURNING 和 insertmanyvalues 的方言:

```py
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)

    # server-side SQL date function generates a new timestamp
    timestamp = mapped_column(DateTime(), server_default=func.now())

    # some other server-side function not named here, such as a trigger,
    # populates a value into this column during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

    # set eager defaults to True.  This is usually optional, as if the
    # backend supports RETURNING + insertmanyvalues, eager defaults
    # will take place regardless on INSERT
    __mapper_args__ = {"eager_defaults": True}

上面的 INSERT 语句未为“timestamp”或“special_identifier”指定客户端端显式值时,将在 RETURNING 子句中包括“timestamp”和“special_identifier”列,以便立即可用。在 PostgreSQL 数据库中,上述表的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  DEFAULT  VALUES  RETURNING  my_table.id,  my_table.timestamp,  my_table.special_identifier

从版本 2.0.0rc1 更改:Mapper.eager_defaults 参数现在默认为新设置 "auto",它将自动使用 RETURNING 在 INSERT 时获取服务器生成的默认值,如果后备数据库同时支持 RETURNING 和 insertmanyvalues。

注意

Mapper.eager_defaults"auto" 值仅适用于 INSERT 语句。即使可用,UPDATE 语句也不会使用 RETURNING,除非 Mapper.eager_defaults 设置为 True。这是因为 UPDATE 没有等价的“insertmanyvalues”功能,因此 UPDATE RETURNING 将要求为每个要 UPDATE 的行单独发出 UPDATE 语句。

情况 2:表中包含触发器生成的值,这些值与 RETURNING 不兼容

Mapper.eager_defaults"auto" 设置意味着支持 RETURNING 的后端通常会在 INSERT 语句中使用 RETURNING 来检索新生成的默认值。但是,存在使用触发器生成的服务器生成值的限制,无法使用 RETURNING:

  • SQL Server 不允许在 INSERT 语句中使用 RETURNING 来检索触发器生成的值;该语句将失败。
  • SQLite 在将 RETURNING 与触发器结合使用时存在限制,因此 RETURNING 子句将不会具有 INSERTed 值可用
  • 其他后端可能在与触发器一起使用 RETURNING 或其他类型的服务器生成的值时存在限制。

要禁用 RETURNING 用于这些值的使用,不仅包括服务器生成的默认值,还要确保 ORM 永远不会对特定表使用 RETURNING,请为映射的Table指定Table.implicit_returning参数为False。使用声明性映射,看起来像这样:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

    # assume a database trigger populates a value into this column
    # during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

    # disable all use of RETURNING for the table
    __table_args__ = {"implicit_returning": False}

在使用 pyodbc 驱动程序的 SQL Server 上,上述表的 INSERT 不会使用 RETURNING,并将使用 SQL Server scope_identity()函数检索新生成的主键值:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  VALUES  (?);  select  scope_identity()

另请参阅

INSERT 行为 - SQL Server 方言获取新生成的主键值的方法的背景

情况 3:非主键、不支持或不需要 RETURNING 或等效功能

这种情况与上述情况 1 相同,除了我们通常不想使用Mapper.eager_defaults,因为在没有支持 RETURNING 的情况下,它的当前实现是发出每行一个 SELECT,这是不高效的。因此,在下面的映射中省略了该参数:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)
    timestamp = mapped_column(DateTime(), server_default=func.now())

    # assume a database trigger populates a value into this column
    # during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

在使用上述映射的记录在不包括 RETURNING 或“insertmanyvalues”支持的后端上 INSERT 之后, “timestamp”和“special_identifier”列将保持为空,并且将在刷新后首次访问时通过第二个 SELECT 语句获取,例如它们被标记为“过期”时。

如果Mapper.eager_defaults显式提供了一个值为True,并且后端数据库不支持 RETURNING 或等效功能,则 ORM 将在 INSERT 语句之后立即发出一个 SELECT 语句以获取新生成的值;如果没有可用的 RETURNING,则 ORM 目前无法批量选择许多新插入的行。这通常是不希望的,因为它会在刷新过程中添加额外的 SELECT 语句,这些语句可能是不必要的。将上述映射与Mapper.eager_defaults标志设置为 True 一起针对 MySQL(不是 MariaDB)使用会在刷新时产生如下 SQL:

代码语言:javascript
复制
INSERT  INTO  my_table  ()  VALUES  ()

-- when eager_defaults **is** used, but RETURNING is not supported
SELECT  my_table.timestamp  AS  my_table_timestamp,  my_table.special_identifier  AS  my_table_special_identifier
FROM  my_table  WHERE  my_table.id  =  %s

未来的 SQLAlchemy 版本可能会试图改进在没有 RETURNING 的情况下的急切默认值的效率,以便在单个 SELECT 语句中批量处理多行。

情况 4:支持主键、RETURNING 或等效功能

具有服务器生成值的主键列必须在 INSERT 后立即获取;ORM 只能访问具有主键值的行,因此如果主键由服务器生成,则 ORM 需要一种在 INSERT 后立即检索该新值的方法。

如上所述,对于整数的“自增”列,以及标记为Identity的列和诸如 PostgreSQL SERIAL 等特殊构造,这些类型由 Core 自动处理;数据库包括用于获取“最后插入的 id”的函数,在不支持 RETURNING 的情况下,以及在支持 RETURNING 的情况下 SQLAlchemy 将使用它。

例如,在 Oracle 中使用标记为Identity的列,RETURNING 会自动用于获取新的主键值:

代码语言:javascript
复制
class MyOracleModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

上述模型在 Oracle 上的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  VALUES  (:data)  RETURNING  my_table.id  INTO  :ret_0

SQLAlchemy 为“data”字段渲染了一个 INSERT,但只在 RETURNING 子句中包括了“id”,以便进行“id”的服务器端生成,并立即返回新值。

对于由服务器端函数或触发器生成的非整数值,以及来自表本身之外的构造的整数值,包括显式序列和触发器,必须在表元数据中标记服务器默认生成。再次以 Oracle 为例,我们可以用上面类似的表来说明使用 Sequence 构造命名的显式序列:

代码语言:javascript
复制
class MyOracleModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(Sequence("my_oracle_seq"), primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

在 Oracle 上,这个版本的模型的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (id,  data)  VALUES  (my_oracle_seq.nextval,  :data)  RETURNING  my_table.id  INTO  :ret_0

在上面的例子中,SQLAlchemy 为主键列渲染了 my_sequence.nextval,以便用于新的主键生成,并且还使用 RETURNING 立即获取新值。

如果数据源不是由简单的 SQL 函数或 Sequence 表示,例如在使用触发器或产生新值的数据库特定数据类型时,可以通过在列定义中使用 FetchedValue 来指示存在生成值的默认值。以下是一个使用 SQL Server TIMESTAMP 列作为主键的模型;在 SQL Server 上,这种数据类型会自动生成新值,因此在表元数据中通过为 Column.server_default 参数指示 FetchedValue 来指示:

代码语言:javascript
复制
class MySQLServerModel(Base):
    __tablename__ = "my_table"

    timestamp: Mapped[datetime.datetime] = mapped_column(
        TIMESTAMP(), server_default=FetchedValue(), primary_key=True
    )
    data: Mapped[str] = mapped_column(String(50))

上述表在 SQL Server 上的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  OUTPUT  inserted.timestamp  VALUES  (?)
情况 5:主键,不支持 RETURNING 或等价的功能

在这个区域,我们正在为像 MySQL 这样的数据库生成行,其中一些生成默认值的方法是在服务器上进行的,但超出了数据库通常的自动增量程序。在这种情况下,我们必须确保 SQLAlchemy 可以“预执行”默认值,这意味着它必须是一个显式的 SQL 表达式。

注意

本节将以 MySQL 的 datetime 值为例,说明多个配方,因为此后端的 datetime 数据类型具有额外的特殊要求,这些要求对于说明很有用。但请记住,MySQL 对于用作主键的任何自动生成的数据类型都需要一个显式的“预执行”默认生成器,而不是通常的单列自增整数值。

具有 DateTime 主键的 MySQL

使用 MySQL 的DateTime列的例子,我们使用“NOW()”SQL 函数添加了一个显式的预执行支持的默认值:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(DateTime(), default=func.now(), primary_key=True)

在上述例子中,我们选择“NOW()”函数来向列传递日期时间值。由上述生成的 SQL 如下:

代码语言:javascript
复制
SELECT  now()  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
('2018-08-09 13:08:46',)
具有 TIMESTAMP 主键的 MySQL

当在 MySQL 中使用TIMESTAMP数据类型时,MySQL 通常会自动将服务器端默认值与此数据类型关联起来。但是,当我们将其用作主键时,核心无法检索新生成的值,除非我们自己执行函数。由于 MySQL 上的TIMESTAMP实际上存储了一个二进制值,因此我们需要在使用“NOW()”时添加额外的“CAST”,以便检索到可以持久化到列中的二进制值:

代码语言:javascript
复制
from sqlalchemy import cast, Binary

class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(
        TIMESTAMP(), default=cast(func.now(), Binary), primary_key=True
    )

除了选择“NOW()”函数之外,在上面的例子中,我们还额外使用Binary数据类型结合cast(),以便返回的值是二进制的。从上面插入的 SQL 看起来像:

代码语言:javascript
复制
SELECT  CAST(now()  AS  BINARY)  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
(b'2018-08-09 13:08:46',)

另请参阅

列插入/更新默认值

关于急切获取客户端调用的用于 INSERT 或 UPDATE 的 SQL 表达式的注意事项

前面的例子表明了使用Column.server_default创建包含默认生成函数的表格的 DDL。

SQLAlchemy 还支持非 DDL 服务器端默认值,如文档中所述客户端调用的 SQL 表达式; 这些“客户端调用的 SQL 表达式”是使用Column.defaultColumn.onupdate参数设置的。

Mapper.eager_defaults 设置为 "auto"True 时,这些 SQL 表达式目前受到 ORM 中与真正的服务器端默认值相同的限制;除非将 FetchedValue 指令与 Column 关联,否则当使用 RETURNING 时,它们不会被自动获取,尽管这些表达式不是 DDL 服务器默认值,而是由 SQLAlchemy 本身积极渲染的。这个限制可能在未来的 SQLAlchemy 版本中得到解决。

FetchedValue 结构可以同时应用于 Column.server_defaultColumn.server_onupdate,以及与 Column.defaultColumn.onupdate 一起使用的 SQL 表达式,例如下面的示例,其中 func.now() 结构用作客户端调用的 SQL 表达式,用于 Column.defaultColumn.onupdate。为了让 Mapper.eager_defaults 的行为包括使用 RETURNING 时获取这些值,需要使用 FetchedValue 来确保进行获取:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)

    created = mapped_column(
        DateTime(), default=func.now(), server_default=FetchedValue()
    )
    updated = mapped_column(
        DateTime(),
        onupdate=func.now(),
        server_default=FetchedValue(),
        server_onupdate=FetchedValue(),
    )

    __mapper_args__ = {"eager_defaults": True}

与上述类似的映射,ORM 渲染的 INSERT 和 UPDATE 的 SQL 将在 RETURNING 子句中包含 createdupdated

代码语言:javascript
复制
INSERT  INTO  my_table  (created)  VALUES  (now())  RETURNING  my_table.id,  my_table.created,  my_table.updated

UPDATE  my_table  SET  updated=now()  WHERE  my_table.id  =  %(my_table_id)s  RETURNING  my_table.updated
```## 使用 INSERT、UPDATE 和 ON CONFLICT(即 upsert)返回 ORM 对象

SQLAlchemy 2.0 包括增强功能,用于发出几种类型的 ORM-enabled INSERT、UPDATE 和 upsert 语句。请查看文档 ORM-Enabled INSERT, UPDATE, and DELETE statements。有关 upsert,请参见 ORM “upsert” Statements。

### 使用 PostgreSQL ON CONFLICT 与 RETURNING 返回 upserted ORM 对象

本节已移至 ORM "upsert"语句。## 分区策略(例如,每个会话多个数据库后端)

### 简单的垂直分区

垂直分区将不同的类、类层次结构或映射表配置到多个数据库中,方法是通过使用`Session`的`Session.binds`参数。该参数接收一个包含任意 ORM 映射类、映射层次结构中的任意类(如声明基类或混合类)、`Table`对象和`Mapper`对象的字典作为键,然后通常引用`Engine`或不太常见的是`Connection`对象作为目标。每当`Session`需要代表特定类型的映射类发出 SQL 以定位适当的数据库连接源时,都会查询该字典:

```py
engine1 = create_engine("postgresql+psycopg2://db1")
engine2 = create_engine("postgresql+psycopg2://db2")

Session = sessionmaker()

# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User: engine1, Account: engine2})

session = Session()

在上述情况下,针对任何一个类的 SQL 操作都将使用与该类相关联的Engine。该功能在读取和写入操作中都是全面的;针对映射到engine1的实体的Query(通过查看请求项列表中的第一个实体来确定)将使用engine1来运行查询。刷新操作将基于每个类使用两个引擎,因为它会刷新UserAccount类型的对象。

在更常见的情况下,通常有基类或混合类可用于区分目的地不同数据库连接的操作。Session.binds参数可以容纳任何任意的 Python 类作为键,如果发现它在特定映射类的__mro__(Python 方法解析顺序)中,则将使用它。假设有两个声明基类代表两个不同的数据库连接:

代码语言:javascript
复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Session

class BaseA(DeclarativeBase):
    pass

class BaseB(DeclarativeBase):
    pass

class User(BaseA): ...

class Address(BaseA): ...

class GameInfo(BaseB): ...

class GameStats(BaseB): ...

Session = sessionmaker()

# all User/Address operations will be on engine 1, all
# Game operations will be on engine 2
Session.configure(binds={BaseA: engine1, BaseB: engine2})

以上,从BaseABaseB继承的类将根据它们是否从任何一个超类继承来路由它们的 SQL 操作到两个引擎之一。在一个类从多个“绑定”超类继承的情况下,将选择目标类层次结构中最高的超类来表示应该使用哪个引擎。

另请参见

Session.binds

多引擎会话的事务协调

使用多个绑定引擎时的一个注意事项是,当提交操作在一个后端成功提交后,可能在另一个后端失败。这是一个不一致性问题,在关系型数据库中通过使用“两阶段事务”解决,它在提交序列中增加了一个额外的“准备”步骤,允许多个数据库在实际完成事务之前同意提交。

由于 DBAPI 中的支持有限,SQLAlchemy 在跨后端的两阶段事务方面的支持也有限。通常,它被认为与 PostgreSQL 后端很好地配合使用,而与 MySQL 后端配合使用的程度较低。但是,当后端支持时,Session 完全能够利用两阶段事务功能,方法是在 sessionmakerSession 中设置 Session.use_twophase 标志。请参阅启用两阶段提交以获取示例。

自定义垂直分区

通过重写 Session.get_bind() 方法可以构建更全面的基于规则的类级别分区。下面我们展示一个自定义的 Session ,它提供以下规则:

  1. 刷新操作以及批量的“更新”和“删除”操作都发送到名为 leader 的引擎。
  2. 所有子类为 MyOtherClass 的对象操作都发生在 other 引擎上。
  3. 所有其他类的读操作都发生在 follower1follower2 数据库的随机选择上。
代码语言:javascript
复制
engines = {
    "leader": create_engine("sqlite:///leader.db"),
    "other": create_engine("sqlite:///other.db"),
    "follower1": create_engine("sqlite:///follower1.db"),
    "follower2": create_engine("sqlite:///follower2.db"),
}

from sqlalchemy.sql import Update, Delete
from sqlalchemy.orm import Session, sessionmaker
import random

class RoutingSession(Session):
    def get_bind(self, mapper=None, clause=None):
        if mapper and issubclass(mapper.class_, MyOtherClass):
            return engines["other"]
        elif self._flushing or isinstance(clause, (Update, Delete)):
            # NOTE: this is for example, however in practice reader/writer
            # splits are likely more straightforward by using two distinct
            # Sessions at the top of a "reader" or "writer" operation.
            # See note below
            return engines["leader"]
        else:
            return engines[random.choice(["follower1", "follower2"])]

上述的 Session 类通过 class_ 参数插入到 sessionmaker 中:

代码语言:javascript
复制
Session = sessionmaker(class_=RoutingSession)

这种方法可以与多个 MetaData 对象结合使用,使用类似使用声明性 __abstract__ 关键字的方法,详细描述在 abstract

注意

虽然上面的示例说明了将特定的 SQL 语句路由到所谓的“领导者”或“跟随者”数据库的方法,这可能不是一个实际的方法,因为它导致了在同一操作中读写之间的不协调事务行为。实际上,最好根据正在进行的整体操作/事务,提前将 Session 构造为“读取器”或“写入器”会话。这样,将要写入数据的操作也将在同一事务范围内发出其读取查询。参见 为 Sessionmaker / Engine 设置隔离级别 中的示例,该示例设置了一个用于使用自动提交连接的“只读”操作的 sessionmaker ,另一个用于“写入”操作,其中包括 DML / COMMIT。

另请参阅

在 SQLAlchemy 中实现 Django 风格的数据库路由器 - 关于更全面的Session.get_bind()的博客文章。

水平分区

水平分区将单个表(或一组表)的行分布到多个数据库中。SQLAlchemy Session 包含对这个概念的支持,但要完全使用它,需要使用 SessionQuery 的子类。这些子类的基本版本可在 水平分片 ORM 扩展中找到。一个使用示例在这里:水平分片。 ## 批量操作

传统特性

SQLAlchemy 2.0 已将“批量插入”和“批量更新”功能集成到 2.0 风格的 Session.execute() 方法中,直接使用了 InsertUpdate 构造。参见文档 ORM-Enabled INSERT, UPDATE, and DELETE statements,包括传统的会话批量 INSERT 方法 ,该文档说明了从旧方法迁移到新方法的示例。 ## 将 SQL 插入/更新表达式嵌入到刷新中

此功能允许将数据库列的值设置为 SQL 表达式,而不是文字值。对于原子更新、调用存储过程等特别有用。您所做的一切就是将表达式分配给属性:

代码语言:javascript
复制
class SomeClass(Base):
    __tablename__ = "some_table"

    # ...

    value = mapped_column(Integer)

someobject = session.get(SomeClass, 5)

# set 'value' attribute to a SQL expression adding one
someobject.value = SomeClass.value + 1

# issues "UPDATE some_table SET value=value+1"
session.commit()

此技术对于 INSERT 和 UPDATE 语句均有效。在 flush/commit 操作之后,上述 someobject 上的 value 属性将过期,因此在下次访问时,新生成的值将从数据库加载。

该功能还具有条件支持,可与主键列一起使用。对于支持 RETURNING 的后端(包括 Oracle、SQL Server、MariaDB 10.5、SQLite 3.35),还可以将 SQL 表达式分配给主键列。这不仅允许评估 SQL 表达式,还允许检索任何在插入时修改主键值的服务器端触发器作为对象主键的一部分成功地检索到 ORM:

代码语言:javascript
复制
class Foo(Base):
    __tablename__ = "foo"
    pk = mapped_column(Integer, primary_key=True)
    bar = mapped_column(Integer)

e = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", echo=True)
Base.metadata.create_all(e)

session = Session(e)

foo = Foo(pk=sql.select(sql.func.coalesce(sql.func.max(Foo.pk) + 1, 1)))
session.add(foo)
session.commit()

在 PostgreSQL 上,上述 Session 将发出以下 INSERT:

代码语言:javascript
复制
INSERT  INTO  foo  (foopk,  bar)  VALUES
((SELECT  coalesce(max(foo.foopk)  +  %(max_1)s,  %(coalesce_2)s)  AS  coalesce_1
FROM  foo),  %(bar)s)  RETURNING  foo.foopk

从版本 1.3 新增:在 ORM flush 期间,SQL 表达式现在可以传递到主键列;如果数据库支持 RETURNING,或者正在使用 pysqlite,则 ORM 将能够将服务器生成的值检索为主键属性的值。

使用 SQL 表达式与会话

SQL 表达式和字符串可以通过其事务上下文在 Session 中执行。这最容易通过 Session.execute() 方法来实现,该方法以与 EngineConnection 相同的方式返回一个 CursorResult

代码语言:javascript
复制
Session = sessionmaker(bind=engine)
session = Session()

# execute a string statement
result = session.execute(text("select * from table where id=:id"), {"id": 7})

# execute a SQL expression construct
result = session.execute(select(mytable).where(mytable.c.id == 7))

Session 当前持有的 Connection 可以通过 Session.connection() 方法访问:

代码语言:javascript
复制
connection = session.connection()

上面的示例涉及到绑定到单个 EngineConnectionSession。要使用绑定到多个引擎或根本没有绑定到引擎的 Session 执行语句,Session.execute()Session.connection() 都接受一个绑定参数字典 Session.execute.bind_arguments,其中可能包括 “mapper”,该参数传递了一个映射类或 Mapper 实例,用于定位所需引擎的正确上下文:

代码语言:javascript
复制
Session = sessionmaker()
session = Session()

# need to specify mapper or class when executing
result = session.execute(
    text("select * from table where id=:id"),
    {"id": 7},
    bind_arguments={"mapper": MyMappedClass},
)

result = session.execute(
    select(mytable).where(mytable.c.id == 7), bind_arguments={"mapper": MyMappedClass}
)

connection = session.connection(MyMappedClass)

从版本 1.4 开始变更:Session.execute()mapperclause 参数现在作为字典的一部分发送,作为 Session.execute.bind_arguments 参数。以前的参数仍然被接受,但此用法已被弃用。

强制将 NULL 值放入具有默认值的列

ORM 认为对象上从未设置的任何属性都是“默认”情况;该属性将从 INSERT 语句中省略:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String(50), nullable=True)

obj = MyObject(id=1)
session.add(obj)
session.commit()  # INSERT with the 'data' column omitted; the database
# itself will persist this as the NULL value

如果在 INSERT 中省略了某列,则该列将被设置为 NULL 值,除非该列设置了默认值,在这种情况下,默认值将被保留。这适用于纯 SQL 视角下具有服务器端默认值的情况,也适用于 SQLAlchemy 的插入行为,无论是客户端默认值还是服务器端默认值:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String(50), nullable=True, server_default="default")

obj = MyObject(id=1)
session.add(obj)
session.commit()  # INSERT with the 'data' column omitted; the database
# itself will persist this as the value 'default'

然而,在 ORM 中,即使将 Python 值 None 显式地分配给对象,这也被视为相同,就像从未分配过值一样:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String(50), nullable=True, server_default="default")

obj = MyObject(id=1, data=None)
session.add(obj)
session.commit()  # INSERT with the 'data' column explicitly set to None;
# the ORM still omits it from the statement and the
# database will still persist this as the value 'default'

上述操作将持久化到 data 列的服务器默认值为 "default",而不是 SQL NULL,即使传递了 None;这是 ORM 的长期行为,许多应用程序都将其视为假设。

那么,如果我们想要在这列中实际放入 NULL 值,即使该列有默认值呢?有两种方法。一种是在每个实例级别上,我们使用 null SQL 构造分配属性:

代码语言:javascript
复制
from sqlalchemy import null

obj = MyObject(id=1, data=null())
session.add(obj)
session.commit()  # INSERT with the 'data' column explicitly set as null();
# the ORM uses this directly, bypassing all client-
# and server-side defaults, and the database will
# persist this as the NULL value

null SQL 结构总是将 SQL NULL 值直接包含在目标 INSERT 语句中。

如果我们希望能够使用 Python 值 None 并且将其作为 NULL 持久化,尽管存在列默认值,我们可以在 ORM 中使用 Core 级别的修饰符 TypeEngine.evaluates_none() 进行配置,该修饰符指示 ORM 应该将值 None 与任何其他值一样对待并将其传递,而不是将其省略为“丢失”的值:

代码语言:javascript
复制
class MyObject(Base):
    __tablename__ = "my_table"
    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(
        String(50).evaluates_none(),  # indicate that None should always be passed
        nullable=True,
        server_default="default",
    )

obj = MyObject(id=1, data=None)
session.add(obj)
session.commit()  # INSERT with the 'data' column explicitly set to None;
# the ORM uses this directly, bypassing all client-
# and server-side defaults, and the database will
# persist this as the NULL value

获取服务器生成的默认值

正如在章节 Server-invoked DDL-Explicit Default Expressions 和 Marking Implicitly Generated Values, timestamps, and Triggered Columns 中介绍的,Core 支持数据库列的概念,其中数据库本身在 INSERT 时生成值,在不太常见的情况下,在 UPDATE 语句中生成值。ORM 功能支持这些列,以便能够在刷新时获取这些新生成的值。在服务器生成的主键列的情况下,由于 ORM 必须在对象持久化后知道其主键,因此需要这种行为。

在绝大多数情况下,由数据库自动生成值的主键列都是简单的整数列,这些列由数据库实现为所谓的“自增”列,或者是与列关联的序列。SQLAlchemy Core 中的每个数据库方言都支持一种检索这些主键值的方法,通常是原生于 Python DBAPI,并且通常这个过程是自动的。关于这一点,有更多的文档说明在 Column.autoincrement 中。

对于不是主键列或不是简单自增整数列的服务器生成列,ORM 要求这些列使用适当的 server_default 指令标记,以允许 ORM 检索此值。然而,并不是所有方法都受到所有后端的支持,因此必须注意使用适当的方法。要回答的两个问题是,1. 此列是否是主键列,2. 数据库是否支持 RETURNING 或等效操作,如 “OUTPUT inserted”;这些是 SQL 短语,它们在调用 INSERT 或 UPDATE 语句时同时返回服务器生成的值。RETURNING 目前由 PostgreSQL、Oracle、MariaDB 10.5、SQLite 3.35 和 SQL Server 支持。

情况 1:非主键,支持 RETURNING 或等效操作

在这种情况下,列应标记为FetchedValue,或者用显式的Column.server_default。ORM 在执行 INSERT 语句时将自动将这些列添加到 RETURNING 子句中,假设 Mapper.eager_defaults 参数设置为 True,或者对于同时支持 RETURNING 和 insertmanyvalues 的方言,保持其默认设置为 "auto"

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)

    # server-side SQL date function generates a new timestamp
    timestamp = mapped_column(DateTime(), server_default=func.now())

    # some other server-side function not named here, such as a trigger,
    # populates a value into this column during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

    # set eager defaults to True.  This is usually optional, as if the
    # backend supports RETURNING + insertmanyvalues, eager defaults
    # will take place regardless on INSERT
    __mapper_args__ = {"eager_defaults": True}

在上面的示例中,如果客户端未为“timestamp”或“special_identifier”指定显式值,则 INSERT 语句将在 RETURNING 子句中包含“timestamp”和“special_identifier”列,以便立即使用。在 PostgreSQL 数据库中,上述表的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  DEFAULT  VALUES  RETURNING  my_table.id,  my_table.timestamp,  my_table.special_identifier

从版本 2.0.0rc1 起更改:Mapper.eager_defaults 参数现在默认为新设置 "auto",如果后端数据库同时支持 RETURNING 和 insertmanyvalues,将自动使用 RETURNING 获取 INSERT 上生成的默认值。

注意

Mapper.eager_defaults"auto" 值仅适用于 INSERT 语句。即使可用,UPDATE 语句也不会使用 RETURNING,除非 Mapper.eager_defaults 设置为 True。这是因为 UPDATE 没有等效的“insertmanyvalues”特性,因此 UPDATE RETURNING 将要求对每个要更新的行分别发出 UPDATE 语句。

情况 2:表包含不兼容于 RETURNING 的触发器生成的值

Mapper.eager_defaults"auto" 设置意味着支持 RETURNING 的后端通常会在 INSERT 语句中使用 RETURNING 以检索新生成的默认值。但是,使用触发器生成的服务器值存在限制,使得无法使用 RETURNING:

  • SQL Server 不允许在 INSERT 语句中使用 RETURNING 来检索触发器生成的值;该语句将失败。
  • SQLite 在将 RETURNING 与触发器组合使用时存在限制,因此 RETURNING 子句将不会包含插入的值
  • 其他后端可能在与触发器一起使用 RETURNING,或者其他类型的服务器生成值时存在限制。

要禁用对这些值的 RETURNING 使用,不仅包括服务器生成的默认值,还要确保 ORM 永远不会与特定表使用 RETURNING,请为映射的 Table 指定 Table.implicit_returningFalse。使用声明性映射如下所示:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

    # assume a database trigger populates a value into this column
    # during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

    # disable all use of RETURNING for the table
    __table_args__ = {"implicit_returning": False}

在使用 pyodbc 驱动程序的 SQL Server 上,对上述表的 INSERT 不会使用 RETURNING,并将使用 SQL Server 的 scope_identity() 函数来检索新生成的主键值:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  VALUES  (?);  select  scope_identity()

另请参阅

INSERT 行为 - 关于 SQL Server 方言获取新生成主键值的方法的背景信息

情况 3:非主键,不支持或不需要 RETURNING 或等效功能

该情况与上述情况 1 相同,但通常我们不希望使用 Mapper.eager_defaults,因为在没有 RETURNING 支持的情况下,其当前实现是为每行发出一个 SELECT,这不是高效的。因此,在下面的映射中省略了该参数:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)
    timestamp = mapped_column(DateTime(), server_default=func.now())

    # assume a database trigger populates a value into this column
    # during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

在不包含 RETURNING 或“insertmanyvalues”支持的后端上插入具有上述映射的记录后,“timestamp” 和 “special_identifier” 列将保持为空,并且在刷新后首次访问时,例如标记为“过期”时,将通过第二个 SELECT 语句获取。

如果 Mapper.eager_defaults 明确提供了值 True,并且后端数据库不支持 RETURNING 或等效功能,则 ORM 将在 INSERT 语句后立即发出 SELECT 语句以获取新生成的值;如果没有 RETURNING 可用,ORM 目前无法批量选择许多新插入的行。这通常是不希望的,因为它会向刷新过程添加额外的 SELECT 语句,这些语句可能是不必要的。在 MySQL(而不是 MariaDB)上使用上述映射与将 Mapper.eager_defaults 标志设置为 True 会导致刷新时生成以下 SQL:

代码语言:javascript
复制
INSERT  INTO  my_table  ()  VALUES  ()

-- when eager_defaults **is** used, but RETURNING is not supported
SELECT  my_table.timestamp  AS  my_table_timestamp,  my_table.special_identifier  AS  my_table_special_identifier
FROM  my_table  WHERE  my_table.id  =  %s

未来的 SQLAlchemy 版本可能会在没有 RETURNING 的情况下,通过批量处理单个 SELECT 语句中的多行来提高急切默认值的效率。

情况 4:主键,支持 RETURNING 或等效功能

具有服务器生成值的主键列必须在 INSERT 后立即获取;ORM 只能访问具有主键值的行,因此如果主键由服务器生成,则 ORM 需要一种在 INSERT 后立即检索该新值的方法。

如上所述,对于整数“自动增量”列,以及标记有 Identity 和特殊构造(如 PostgreSQL SERIAL)的列,Core 会自动处理这些类型;数据库包括用于获取“最后插入 id”的函数,在不支持 RETURNING 的情况下,以及支持 RETURNING 的情况下 SQLAlchemy 将使用该函数。

例如,在 Oracle 中,如果将列标记为 Identity,则自动使用 RETURNING 获取新的主键值:

代码语言:javascript
复制
class MyOracleModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

如上所述,上述模型在 Oracle 上的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  VALUES  (:data)  RETURNING  my_table.id  INTO  :ret_0

SQLAlchemy 为“data”字段渲染了一个 INSERT,但在 RETURNING 子句中仅包含了“id”,以便在服务器端生成“id”,并立即返回新值。

对于由服务器端函数或触发器生成的非整数值,以及来自表格本身之外的结构(包括显式序列和触发器)的整数值,必须在表格元数据中标记服务器默认生成。再次以 Oracle 为例,我们可以举例说明一个类似上述的表格,使用 Sequence 构造命名一个显式序列:

代码语言:javascript
复制
class MyOracleModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(Sequence("my_oracle_seq"), primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

在 Oracle 上,对于此模型的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (id,  data)  VALUES  (my_oracle_seq.nextval,  :data)  RETURNING  my_table.id  INTO  :ret_0

在上述情况中,SQLAlchemy 为主键列渲染了 my_sequence.nextval,以便用于新的主键生成,并且还使用 RETURNING 立即获取新值。

如果数据源不是由简单的 SQL 函数或 Sequence 表示,例如在使用触发器或生成新值的数据库特定数据类型时,可以通过在列定义中使用 FetchedValue 来指示值生成默认值的存在。下面是一个使用 SQL Server TIMESTAMP 列作为主键的模型;在 SQL Server 上,此数据类型会自动生成新值,因此在表格元数据中通过为 Column.server_default 参数指示 FetchedValue 来表示这一点:

代码语言:javascript
复制
class MySQLServerModel(Base):
    __tablename__ = "my_table"

    timestamp: Mapped[datetime.datetime] = mapped_column(
        TIMESTAMP(), server_default=FetchedValue(), primary_key=True
    )
    data: Mapped[str] = mapped_column(String(50))

在 SQL Server 上,对于上述表格的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  OUTPUT  inserted.timestamp  VALUES  (?)
情况 5:不支持主键、RETURNING 或等效功能。

在此领域,我们正在为 MySQL 等数据库生成行,其中服务器上正在发生一些默认生成的手段,但这些手段不在数据库的通常自增例程中。在这种情况下,我们必须确保 SQLAlchemy 可以“预先执行”默认值,这意味着它必须是一个明确的 SQL 表达式。

注意

本节将说明涉及 MySQL 日期时间值的多个配方,因为该后端的日期时间数据类型具有额外的特殊要求,这些要求对于说明非常有用。但是请注意,除了通常的单列自增整数值之外,MySQL 需要为任何用作主键的自动生成数据类型显式的“预执行”默认生成器。

具有 DateTime 主键的 MySQL

以 MySQL 的DateTime列为例,我们使用“NOW()”SQL 函数添加了一个明确的预执行支持的默认值:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(DateTime(), default=func.now(), primary_key=True)

在上述情况下,我们选择“NOW()”函数以向列传递日期时间值。上述生成的 SQL 是:

代码语言:javascript
复制
SELECT  now()  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
('2018-08-09 13:08:46',)
具有 TIMESTAMP 主键的 MySQL

当使用 MySQL 的TIMESTAMP数据类型时,MySQL 通常会自动将服务器端默认与该数据类型关联起来。但是,当我们将其用作主键时,Core 无法检索新生成的值,除非我们自己执行该函数。由于 MySQL 上的TIMESTAMP实际上存储了一个二进制值,因此我们需要在“NOW()”的使用中添加一个额外的“CAST”,以便检索到可以持久化到列中的二进制值:

代码语言:javascript
复制
from sqlalchemy import cast, Binary

class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(
        TIMESTAMP(), default=cast(func.now(), Binary), primary_key=True
    )

以上,在选择“NOW()”函数的同时,我们还使用了Binary数据类型结合cast(),以便返回的值是二进制的。在 INSERT 中从上述渲染的 SQL 如下所示:

代码语言:javascript
复制
SELECT  CAST(now()  AS  BINARY)  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
(b'2018-08-09 13:08:46',)

另见

列插入/更新默认值

注意事项:对于用于 INSERT 或 UPDATE 的急切提取客户端调用的 SQL 表达式

上述示例指示了使用Column.server_default创建包含其 DDL 中的默认生成函数的表。

SQLAlchemy 也支持非 DDL 服务器端的默认设置,如客户端调用的 SQL 表达式文档中所述;这些“客户端调用的 SQL 表达式”是使用Column.defaultColumn.onupdate参数设置的。

目前,ORM 中的这些 SQL 表达式受到与真正的服务器端默认值相同的限制;当 Mapper.eager_defaults 设置为 "auto"True 时,除非 FetchedValue 指令与 Column 相关联,否则它们不会被 RETURNING 急切地获取,尽管这些表达式不是 DDL 服务器默认值,并且由 SQLAlchemy 本身主动渲染。这个限制可能在未来的 SQLAlchemy 版本中得到解决。

FetchedValue 构造可以同时应用于 Column.server_defaultColumn.server_onupdate,就像下面的例子中使用 func.now() 构造作为客户端调用的 SQL 表达式用于 Column.defaultColumn.onupdate 一样。为了使 Mapper.eager_defaults 的行为包括在可用时使用 RETURNING 获取这些值,Column.server_defaultColumn.server_onupdateFetchedValue 一起使用以确保获取发生:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)

    created = mapped_column(
        DateTime(), default=func.now(), server_default=FetchedValue()
    )
    updated = mapped_column(
        DateTime(),
        onupdate=func.now(),
        server_default=FetchedValue(),
        server_onupdate=FetchedValue(),
    )

    __mapper_args__ = {"eager_defaults": True}

使用类似上面的映射,ORM 渲染的 INSERT 和 UPDATE 的 SQL 将在 RETURNING 子句中包括createdupdated

代码语言:javascript
复制
INSERT  INTO  my_table  (created)  VALUES  (now())  RETURNING  my_table.id,  my_table.created,  my_table.updated

UPDATE  my_table  SET  updated=now()  WHERE  my_table.id  =  %(my_table_id)s  RETURNING  my_table.updated
情况 1:非主键,支持 RETURNING 或等效功能

在这种情况下,应将列标记为FetchedValue或具有显式的Column.server_default。如果Mapper.eager_defaults参数设置为True,或者对于支持 RETURNING 以及 insertmanyvalues 的方言,默认设置为"auto",ORM 将在执行 INSERT 语句时自动将这些列添加到 RETURNING 子句中:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)

    # server-side SQL date function generates a new timestamp
    timestamp = mapped_column(DateTime(), server_default=func.now())

    # some other server-side function not named here, such as a trigger,
    # populates a value into this column during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

    # set eager defaults to True.  This is usually optional, as if the
    # backend supports RETURNING + insertmanyvalues, eager defaults
    # will take place regardless on INSERT
    __mapper_args__ = {"eager_defaults": True}

在上述情况下,未在客户端指定“timestamp”或“special_identifier”的显式值的 INSERT 语句将包括“timestamp”和“special_identifier”列在 RETURNING 子句中,以便立即使用。在 PostgreSQL 数据库上,上述表的 INSERT 将如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  DEFAULT  VALUES  RETURNING  my_table.id,  my_table.timestamp,  my_table.special_identifier

从版本 2.0.0rc1 开始更改:Mapper.eager_defaults参数现在默认为新设置"auto",如果支持 RETURNING 以及 insertmanyvalues 的后端数据库,则会自动使用 RETURNING 来获取 INSERT 时的服务器生成默认值。

注意

Mapper.eager_defaults"auto"值仅适用于 INSERT 语句。即使可用,UPDATE 语句也不会使用 RETURNING,除非将Mapper.eager_defaults设置为True。这是因为 UPDATE 没有等效的“insertmanyvalues”特性,因此 UPDATE RETURNING 将要求为每个被 UPDATE 的行分别发出 UPDATE 语句。

情况 2:表包含与 RETURNING 不兼容的触发器生成的值

Mapper.eager_defaults"auto"设置意味着支持 RETURNING 的后端通常会在 INSERT 语句中使用 RETURNING 来检索新生成的默认值。但是,存在使用触发器生成的服务器生成值的限制,因此不能使用 RETURNING:

  • SQL Server 不允许在 INSERT 语句中使用 RETURNING 来检索触发器生成的值;该语句将失败。
  • SQLite 在与触发器结合使用 RETURNING 时存在限制,因此 RETURNING 子句将无法获取已插入的值。
  • 其他后端可能在与触发器或其他类型的服务器生成值结合使用 RETURNING 时存在限制。

要禁用用于此类值的 RETURNING 的使用,包括不仅用于服务器生成的默认值而且确保 ORM 永远不会使用 RETURNING 与特定表,指定 Table.implicit_returningFalse 对于映射的 Table。使用声明性映射,看起来像这样:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

    # assume a database trigger populates a value into this column
    # during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

    # disable all use of RETURNING for the table
    __table_args__ = {"implicit_returning": False}

在使用 pyodbc 驱动程序的 SQL Server 上,对于上述表的 INSERT 不会使用 RETURNING,并且将使用 SQL Server scope_identity() 函数来检索新生成的主键值:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  VALUES  (?);  select  scope_identity()

另请参阅

INSERT 行为 - 关于 SQL Server 方言获取新生成的主键值的方法的背景

情况 3:非主键,不支持或不需要 RETURNING 或等效功能

该情况与上面的情况 1 相同,只是我们通常不想使用 Mapper.eager_defaults,因为在没有 RETURNING 支持的情况下,其当前实现是发出每行一个 SELECT,这是不高效的。因此,在下面的映射中省略了该参数:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)
    timestamp = mapped_column(DateTime(), server_default=func.now())

    # assume a database trigger populates a value into this column
    # during INSERT
    special_identifier = mapped_column(String(50), server_default=FetchedValue())

在上述映射中插入记录后,在不包括 RETURNING 或“insertmanyvalues”支持的后端上,“timestamp” 和 “special_identifier” 列将保持为空,并且在刷新后首次访问时将通过第二个 SELECT 语句获取,例如,它们被标记为“过期”时。

如果 Mapper.eager_defaults 明确提供了值 True,并且后端数据库不支持 RETURNING 或等效功能,则 ORM 将在 INSERT 语句后立即发出 SELECT 语句,以获取新生成的值;如果没有可用的 RETURNING,ORM 目前无法批量选择许多新插入的行。这通常是不可取的,因为它会向刷新过程添加额外的 SELECT 语句,这些语句可能是不需要的。使用上述映射,针对 MySQL(不是 MariaDB)将 Mapper.eager_defaults 标志设置为 True 在刷新时会产生类似以下的 SQL:

代码语言:javascript
复制
INSERT  INTO  my_table  ()  VALUES  ()

-- when eager_defaults **is** used, but RETURNING is not supported
SELECT  my_table.timestamp  AS  my_table_timestamp,  my_table.special_identifier  AS  my_table_special_identifier
FROM  my_table  WHERE  my_table.id  =  %s

未来的 SQLAlchemy 版本可能会在没有 RETURNING 的情况下寻求改进急切默认值的效率,以在单个 SELECT 语句中批量处理多行。

情况 4:主键,支持 RETURNING 或等效功能

具有服务器生成值的主键列必须在 INSERT 后立即获取;ORM 只能访问具有主键值的行,因此如果主键由服务器生成,则 ORM 需要一种在 INSERT 后立即检索该新值的方法。

如上所述,对于整数“自增”列,以及标记为Identity的列和特殊构造,例如 PostgreSQL 的 SERIAL,这些类型将由核心自动处理;数据库包括获取“最后插入的 id”函数,其中不支持 RETURNING,而在支持 RETURNING 的情况下,SQLAlchemy 将使用它。

例如,使用 Oracle 并将列标记为Identity,RETURNING 将自动用于获取新的主键值:

代码语言:javascript
复制
class MyOracleModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(Identity(), primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

如上模型在 Oracle 上的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  VALUES  (:data)  RETURNING  my_table.id  INTO  :ret_0

SQLAlchemy 渲染“data”字段的 INSERT,但仅在 RETURNING 子句中包含“id”,以便在服务器端生成“id”并立即返回新值。

对于由服务器端函数或触发器生成的非整数值,以及来自表本身之外的构造的整数值,包括显式序列和触发器,必须在表元数据中标记服务器默认生成。再次以 Oracle 为例,我们可以用Sequence构造说明一个类似的表:

代码语言:javascript
复制
class MyOracleModel(Base):
    __tablename__ = "my_table"

    id: Mapped[int] = mapped_column(Sequence("my_oracle_seq"), primary_key=True)
    data: Mapped[str] = mapped_column(String(50))

Oracle 上此模型的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (id,  data)  VALUES  (my_oracle_seq.nextval,  :data)  RETURNING  my_table.id  INTO  :ret_0

在上述情况下,SQLAlchemy 渲染my_sequence.nextval用于主键列的新主键生成,并且还使用 RETURNING 立即获取新值。

如果数据源不是由简单的 SQL 函数或Sequence表示,例如使用触发器或生成新值的数据库特定数据类型,可以通过在列定义中使用FetchedValue来指示值生成的默认值。下面是一个使用 SQL Server TIMESTAMP 列作为主键的模型;在 SQL Server 上,此数据类型会自动生成新值,因此在表元数据中通过为Column.server_default参数指定FetchedValue来指示:

代码语言:javascript
复制
class MySQLServerModel(Base):
    __tablename__ = "my_table"

    timestamp: Mapped[datetime.datetime] = mapped_column(
        TIMESTAMP(), server_default=FetchedValue(), primary_key=True
    )
    data: Mapped[str] = mapped_column(String(50))

SQL Server 上上述表的 INSERT 如下所示:

代码语言:javascript
复制
INSERT  INTO  my_table  (data)  OUTPUT  inserted.timestamp  VALUES  (?)
情况 5:不支持主键、RETURNING 或等效项。

在这个领域,我们为像 MySQL 这样的数据库生成行,其中服务器上正在发生某种默认生成的方法,但是超出了数据库的通常自动增量例程。在这种情况下,我们必须确保 SQLAlchemy 可以“预执行”默认值,这意味着它必须是一个显式的 SQL 表达式。

本节将说明 MySQL 中涉及日期时间值的多个示例,因为此后端的日期时间数据类型具有有用的额外特殊要求。但是请记住,MySQL 对于用作主键的任何自动生成的数据类型都需要明确的“预执行”默认生成器,除了通常的单列自增整数值。

MySQL 使用 DateTime 主键

使用 MySQL 的DateTime列作为例子,我们使用“NOW()”SQL 函数添加一个明确的预执行支持的默认值:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(DateTime(), default=func.now(), primary_key=True)

在上面的例子中,我们选择“NOW()”函数将日期时间值传递给列。由上述生成的 SQL 是:

代码语言:javascript
复制
SELECT  now()  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
('2018-08-09 13:08:46',)
MySQL 使用 TIMESTAMP 主键

当在 MySQL 中使用TIMESTAMP数据类型时,MySQL 通常会自动将服务器端默认值与此数据类型关联起来。但是,当我们将其用作主键时,Core 无法检索到新生成的值,除非我们自己执行该函数。由于 MySQL 上的TIMESTAMP实际上存储的是二进制值,因此我们需要在“NOW()”的使用中添加额外的“CAST”,以便检索到可持久化到列中的二进制值:

代码语言:javascript
复制
from sqlalchemy import cast, Binary

class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(
        TIMESTAMP(), default=cast(func.now(), Binary), primary_key=True
    )

上述,除了选择“NOW()”函数外,我们还额外利用Binary数据类型与cast()结合使用,以便返回值是二进制的。在 INSERT 中生成的 SQL 如下所示:

代码语言:javascript
复制
SELECT  CAST(now()  AS  BINARY)  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
(b'2018-08-09 13:08:46',)

另请参阅

列的 INSERT/UPDATE 默认值

MySQL 使用 DateTime 主键

使用 MySQL 的DateTime列作为例子,我们使用“NOW()”SQL 函数添加一个明确的预执行支持的默认值:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(DateTime(), default=func.now(), primary_key=True)

在上面的例子中,我们选择“NOW()”函数将日期时间值传递给列。由上述生成的 SQL 是:

代码语言:javascript
复制
SELECT  now()  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
('2018-08-09 13:08:46',)
MySQL 使用 TIMESTAMP 主键

当在 MySQL 中使用TIMESTAMP数据类型时,MySQL 通常会自动将服务器端默认值与此数据类型关联起来。但是,当我们将其用作主键时,Core 无法检索到新生成的值,除非我们自己执行该函数。由于 MySQL 上的TIMESTAMP实际上存储的是二进制值,因此我们需要在“NOW()”的使用中添加额外的“CAST”,以便检索到可持久化到列中的二进制值:

代码语言:javascript
复制
from sqlalchemy import cast, Binary

class MyModel(Base):
    __tablename__ = "my_table"

    timestamp = mapped_column(
        TIMESTAMP(), default=cast(func.now(), Binary), primary_key=True
    )

在上面的示例中,除了选择“NOW()”函数外,我们还使用Binary数据类型结合cast(),以使返回的值是二进制的。在 INSERT 中从上面渲染的 SQL 如下所示:

代码语言:javascript
复制
SELECT  CAST(now()  AS  BINARY)  AS  anon_1
INSERT  INTO  my_table  (timestamp)  VALUES  (%s)
(b'2018-08-09 13:08:46',)

另请参阅

列插入/更新默认值

关于急切获取用于 INSERT 或 UPDATE 的客户端调用的 SQL 表达式的注意事项

前面的示例表明了使用Column.server_default创建包含默认生成函数的表的方法。

SQLAlchemy 也支持非 DDL 服务器端默认值,如客户端调用的 SQL 表达式文档所述;这些“客户端调用的 SQL 表达式”是使用Column.defaultColumn.onupdate参数设置的。

这些 SQL 表达式目前受 ORM 中与真正的服务器端默认值发生的相同限制的约束;当Mapper.eager_defaults设置为"auto"True时,它们不会被急切地获取到 RETURNING 中,除非FetchedValue指令与Column关联,即使这些表达式不是 DDL 服务器默认值,而是由 SQLAlchemy 本身主动渲染的。这个限制可能在未来的 SQLAlchemy 版本中得到解决。

FetchedValue 构造可以同时应用于 Column.server_defaultColumn.server_onupdate,与 Column.defaultColumn.onupdate 一起使用 SQL 表达式,例如下面的示例中,func.now() 构造被用作客户端调用的 SQL 表达式,用于 Column.defaultColumn.onupdate。为了使 Mapper.eager_defaults 的行为包括使用 RETURNING 在可用时获取这些值,需要使用 Column.server_defaultColumn.server_onupdateFetchedValue 以确保获取发生:

代码语言:javascript
复制
class MyModel(Base):
    __tablename__ = "my_table"

    id = mapped_column(Integer, primary_key=True)

    created = mapped_column(
        DateTime(), default=func.now(), server_default=FetchedValue()
    )
    updated = mapped_column(
        DateTime(),
        onupdate=func.now(),
        server_default=FetchedValue(),
        server_onupdate=FetchedValue(),
    )

    __mapper_args__ = {"eager_defaults": True}

与上述类似的映射,ORM 渲染的 INSERT 和 UPDATE 的 SQL 将在 RETURNING 子句中包括 createdupdated

代码语言:javascript
复制
INSERT  INTO  my_table  (created)  VALUES  (now())  RETURNING  my_table.id,  my_table.created,  my_table.updated

UPDATE  my_table  SET  updated=now()  WHERE  my_table.id  =  %(my_table_id)s  RETURNING  my_table.updated

使用 INSERT、UPDATE 和 ON CONFLICT(即 upsert)返回 ORM 对象

SQLAlchemy 2.0 包括增强功能,用于发出几种类型的启用 ORM 的 INSERT、UPDATE 和 upsert 语句。查看文档 ORM-Enabled INSERT, UPDATE, and DELETE statements 以获取文档。有关 upsert,请参见 ORM “upsert” Statements。

使用 PostgreSQL ON CONFLICT 与 RETURNING 返回 upserted ORM 对象

本节已移至 ORM “upsert” Statements。

使用 PostgreSQL ON CONFLICT 与 RETURNING 返回 upserted ORM 对象

本节已移至 ORM “upsert” Statements。

分区策略(例如,每个会话使用多个数据库后端)

简单的垂直分区

垂直分区通过配置SessionSession.binds 参数,将不同的类、类层次结构或映射表放置在多个数据库中。此参数接收一个包含任意组合的 ORM 映射类、映射层次结构中的任意类(如声明性基类或混合类)、Table 对象和Mapper 对象作为键的字典,然后通常引用Engine 或较不常见的 Connection 对象作为目标。每当Session需要代表特定类型的映射类发出 SQL 以定位适当的数据库连接源时,就会查询该字典:

代码语言:javascript
复制
engine1 = create_engine("postgresql+psycopg2://db1")
engine2 = create_engine("postgresql+psycopg2://db2")

Session = sessionmaker()

# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User: engine1, Account: engine2})

session = Session()

在上面,针对任一类的 SQL 操作都将使用与该类链接的Engine。该功能涵盖了读写操作;针对映射到engine1的实体的 Query(通过查看请求的项目列表中的第一个实体确定)将使用engine1来运行查询。刷新操作将根据每个类使用两个引擎,因为它刷新了UserAccount类型的对象。

在更常见的情况下,通常有基础类或混合类可用于区分不同数据库连接的操作。Session.binds 参数可以容纳任何任意的 Python 类作为键,如果发现它在特定映射类的__mro__(Python 方法解析顺序)中,则会使用该键。假设两个声明性基类分别表示两个不同的数据库连接:

代码语言:javascript
复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Session

class BaseA(DeclarativeBase):
    pass

class BaseB(DeclarativeBase):
    pass

class User(BaseA): ...

class Address(BaseA): ...

class GameInfo(BaseB): ...

class GameStats(BaseB): ...

Session = sessionmaker()

# all User/Address operations will be on engine 1, all
# Game operations will be on engine 2
Session.configure(binds={BaseA: engine1, BaseB: engine2})

在上面,从BaseABaseB继承的类将根据它们是否继承自任何超类来将它们的 SQL 操作路由到两个引擎中的一个。对于从多个“绑定”超类继承的类的情况,将选择目标类层次结构中最高的超类来表示应使用哪个引擎。

另请参阅

Session.binds

为多引擎会话协调事务

使用多个绑定引擎的一个注意事项是,如果提交操作在一个后端成功提交后在另一个后端失败,则可能会出现问题。这是一个一致性问题,在关系数据库中通过“两阶段事务”解决,它在提交序列中添加了一个额外的“准备”步骤,允许多个数据库在实际完成事务之前同意提交。

由于 DBAPI 中的支持有限,SQLAlchemy 对跨后端的两阶段事务的支持也有限。通常来说,它已知与 PostgreSQL 后端一起工作良好,与 MySQL 后端一起工作效果较差。然而,当后端支持时,Session 完全能够利用两阶段事务功能,方法是在 sessionmakerSession 中设置 Session.use_twophase 标志。参见启用两阶段提交以获取示例。

自定义垂直分区

更全面的基于规则的类级分区可以通过覆盖 Session.get_bind() 方法来构建。下面我们演示了一个自定义的 Session,它提供以下规则:

  1. 刷新操作以及批量的“更新”和“删除”操作都传递到名为 leader 的引擎。
  2. 所有子类为 MyOtherClass 的对象的操作都发生在 other 引擎上。
  3. 对于所有其他类的读取操作都发生在随机选择的 follower1follower2 数据库上。
代码语言:javascript
复制
engines = {
    "leader": create_engine("sqlite:///leader.db"),
    "other": create_engine("sqlite:///other.db"),
    "follower1": create_engine("sqlite:///follower1.db"),
    "follower2": create_engine("sqlite:///follower2.db"),
}

from sqlalchemy.sql import Update, Delete
from sqlalchemy.orm import Session, sessionmaker
import random

class RoutingSession(Session):
    def get_bind(self, mapper=None, clause=None):
        if mapper and issubclass(mapper.class_, MyOtherClass):
            return engines["other"]
        elif self._flushing or isinstance(clause, (Update, Delete)):
            # NOTE: this is for example, however in practice reader/writer
            # splits are likely more straightforward by using two distinct
            # Sessions at the top of a "reader" or "writer" operation.
            # See note below
            return engines["leader"]
        else:
            return engines[random.choice(["follower1", "follower2"])]

上述Session 类是通过 sessionmakerclass_ 参数来插入的:

代码语言:javascript
复制
Session = sessionmaker(class_=RoutingSession)

这种方法可以与多个 MetaData 对象结合使用,使用类似于使用声明性 __abstract__ 关键字的方法,如 abstract 中所述。

注意

上述示例说明了根据 SQL 语句是否期望写入数据将特定 SQL 语句路由到所谓的“主”或“从”数据库,但这可能不是一个实用的方法,因为它会导致在同一操作中读取和写入之间存在不协调的事务行为。实践中,最好在整个操作/事务进行的基础上,提前构建Session作为“读取者”或“写入者”会话。这样,将要写入数据的操作也会在同一个事务范围内发出其读取查询。有关在sessionmaker中设置“只读”操作的配方,使用自动提交连接,以及用于包含 DML/COMMIT 的“写入”操作的另一个配方,请参阅为 Sessionmaker / Engine 设置隔离级别的示例。

另请参阅

SQLAlchemy 中的 Django 风格数据库路由器 - 关于Session.get_bind()更全面示例的博文

水平分区

水平分区将单个表(或一组表)的行分区到多个数据库中。SQLAlchemy 的Session包含对这个概念的支持,但要完全使用它,需要使用SessionQuery子类。这些子类的基本版本可在水平分区 ORM 扩展中找到。一个使用示例位于:水平分区。

简单的垂直分区

垂直分区将不同的类、类层次结构或映射表配置到多个数据库中,通过配置SessionSession.binds参数。该参数接收一个字典,其中包含任意组合的 ORM 映射类、映射层次结构内的任意类(例如声明基类或混合类)、Table对象和Mapper对象作为键,这些键通常引用Engine或更少见的情况下引用Connection对象作为目标。每当Session需要代表特定类型的映射类发出 SQL 以定位数据库连接的适当源时,就会查询该字典:

代码语言:javascript
复制
engine1 = create_engine("postgresql+psycopg2://db1")
engine2 = create_engine("postgresql+psycopg2://db2")

Session = sessionmaker()

# bind User operations to engine 1, Account operations to engine 2
Session.configure(binds={User: engine1, Account: engine2})

session = Session()

在上述情况下,针对任一类的 SQL 操作将使用与该类链接的Engine。该功能在读写操作中都是全面的;针对映射到engine1的实体的Query(通过查看请求的项目列表中的第一个实体来确定)将使用engine1来运行查询。刷新操作将基于每个类使用两个引擎,因为它会刷新UserAccount类型的对象。

在更常见的情况下,通常有基类或混合类可用于区分命令操作的目标数据库连接。Session.binds参数可以接受任何任意的 Python 类作为键,如果在特定映射类的__mro__(Python 方法解析顺序)中找到,则会使用该键。假设有两个声明基类代表两个不同的数据库连接:

代码语言:javascript
复制
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Session

class BaseA(DeclarativeBase):
    pass

class BaseB(DeclarativeBase):
    pass

class User(BaseA): ...

class Address(BaseA): ...

class GameInfo(BaseB): ...

class GameStats(BaseB): ...

Session = sessionmaker()

# all User/Address operations will be on engine 1, all
# Game operations will be on engine 2
Session.configure(binds={BaseA: engine1, BaseB: engine2})

在上述情况下,从BaseABaseB继承的类将根据它们是否继承自其中任何一个超类而将其 SQL 操作路由到两个引擎中的一个。对于从多个“绑定”超类继承的类,将选择目标类层次结构中最高的超类来表示应该使用哪个引擎。

另请参阅

Session.binds

多引擎会话的事务协调

在使用多个绑定引擎的情况下,有一个需要注意的地方是,在一个提交操作在一个后端成功提交后,另一个后端可能失败。这是一个一致性问题,在关系型数据库中通过“两阶段事务”解决,该事务将一个额外的“准备”步骤添加到提交序列中,允许多个数据库在实际完成事务之前同意提交。

由于 DBAPI 的支持有限,SQLAlchemy 对跨后端的两阶段事务的支持也有限。最典型的是,它在 PostgreSQL 后端上运行良好,并且在 MySQL 后端上的支持较少。但是,当后端支持时,Session完全能够利用两阶段事务功能,方法是在sessionmakerSession中设置Session.use_twophase标志。参见启用两阶段提交以获取示例。

自定义垂直分区

可以通过重写Session.get_bind()方法来构建更全面的基于规则的类级分区。以下是一个自定义Session的示例,提供以下规则:

  1. 刷新操作以及批量“更新”和“删除”操作将传送到名为leader的引擎。
  2. 所有子类化MyOtherClass的对象操作都发生在other引擎上。
  3. 所有其他类的读取操作都在follower1follower2数据库的随机选择上进行。
代码语言:javascript
复制
engines = {
    "leader": create_engine("sqlite:///leader.db"),
    "other": create_engine("sqlite:///other.db"),
    "follower1": create_engine("sqlite:///follower1.db"),
    "follower2": create_engine("sqlite:///follower2.db"),
}

from sqlalchemy.sql import Update, Delete
from sqlalchemy.orm import Session, sessionmaker
import random

class RoutingSession(Session):
    def get_bind(self, mapper=None, clause=None):
        if mapper and issubclass(mapper.class_, MyOtherClass):
            return engines["other"]
        elif self._flushing or isinstance(clause, (Update, Delete)):
            # NOTE: this is for example, however in practice reader/writer
            # splits are likely more straightforward by using two distinct
            # Sessions at the top of a "reader" or "writer" operation.
            # See note below
            return engines["leader"]
        else:
            return engines[random.choice(["follower1", "follower2"])]

上述Session类是通过向sessionmaker传递class_参数来插入的:

代码语言:javascript
复制
Session = sessionmaker(class_=RoutingSession)

这种方法可以与多个MetaData对象结合使用,例如使用声明性的__abstract__关键字的方法,如在 abstract 中所述。

注意

尽管上面的示例说明了将特定的 SQL 语句路由到基于语句是否期望写入数据的所谓 “leader” 或 “follower” 数据库,但这可能不是一种实际的方法,因为它导致在同一操作中读取和写入之间的不协调事务行为。实际上,最好是根据正在进行的整体操作 / 事务,提前将 Session 构造为 “读取者” 或 “写入者” 会话。这样,将要写入数据的操作也会在同一个事务范围内发出其读取查询。请参阅 为 Sessionmaker / Engine 设置隔离 中的示例,该示例设置了一个用于 “只读” 操作的 sessionmaker ,使用自动提交连接,另一个用于包含 DML / COMMIT 的 “写入” 操作。

另请参阅

SQLAlchemy 中的 Django 风格数据库路由器 - 有关 Session.get_bind() 的更全面示例的博客文章

水平分区

水平分区将单个表(或一组表)的行跨多个数据库进行分区。SQLAlchemy Session 包含对此概念的支持,但要充分利用它,需要使用 SessionQuery 的子类。这些子类的基本版本在 水平分片 ORM 扩展中可用。使用示例位于:水平分片。

批量操作

遗留特性

SQLAlchemy 2.0 将 Session 的“批量插入”和“批量更新”功能集成到了 2.0 风格的 Session.execute() 方法中,直接使用了 InsertUpdate 构造。请参阅 ORM 启用的 INSERT、UPDATE 和 DELETE 语句 文档,包括 遗留 Session 批量 INSERT 方法 ,其中说明了从旧方法迁移到新方法的示例。

上下文/线程本地会话

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

回顾一下何时构建会话,何时提交,何时关闭?一节中,介绍了“会话范围”的概念,强调了在 Web 应用程序中链接Session的范围与 Web 请求的范围之间的实践。大多数现代 Web 框架都包括集成工具,以便自动管理Session的范围,并且应该使用这些工具,只要它们可用。

SQLAlchemy 包括其自己的辅助对象,它有助于建立用户定义的Session范围。它也被第三方集成系统用于帮助构建它们的集成方案。

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

警告

scoped_session注册表默认使用 Python 的threading.local()来跟踪Session实例。这不一定与所有应用服务器兼容,特别是那些使用绿色线程或其他替代形式的并发控制的服务器,这可能导致在中高并发情况下使用时出现竞争条件(例如,随机发生的故障)。请阅读下面的线程局部范围和在 Web 应用程序中使用线程局部范围以更充分地理解使用threading.local()来跟踪Session对象的影响,并在使用不基于传统线程的应用服务器时考虑更明确的范围。

注意

scoped_session对象是许多 SQLAlchemy 应用程序中非常流行和有用的对象。然而,重要的是要注意,它只提供了解决Session管理问题的一个方法。如果你对 SQLAlchemy 还不熟悉,特别是如果“线程本地变量”这个术语对你来说很陌生,我们建议你如果可能的话,首先熟悉一下诸如Flask-SQLAlchemyzope.sqlalchemy之类的现成集成系统。

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

代码语言:javascript
复制
>>> from sqlalchemy.orm import scoped_session
>>> from sqlalchemy.orm import sessionmaker

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

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

代码语言:javascript
复制
>>> some_session = Session()

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

代码语言:javascript
复制
>>> some_other_session = Session()
>>> some_session is some_other_session
True

这种模式允许应用程序的不同部分调用全局的scoped_session,这样所有这些区域就可以在不需要显式传递的情况下共享同一个会话。我们在注册表中建立的Session将保持不变,直到我们显式告诉注册表将其销毁,方法是调用scoped_session.remove()

代码语言:javascript
复制
>>> Session.remove()

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

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

代码语言:javascript
复制
>>> new_session = Session()
>>> new_session is some_session
False

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

隐式方法访问

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

代码语言:javascript
复制
Session = scoped_session(some_factory)

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

上述代码实现了通过调用注册表获取当前 Session 然后使用该 Session 的相同任务。

线程本地作用域

熟悉多线程编程的用户会注意到,将任何东西表示为全局变量通常是一个坏主意,因为这意味着全局对象将被许多线程同时访问。Session 对象完全设计成以非并发方式使用,从多线程的角度来看,这意味着“一次只能在一个线程中”。因此,我们上面对 scoped_session 的使用示例,其中相同的 Session 对象在多次调用中保持不变,暗示着需要某种处理方式,以使多个线程中的多次调用实际上不会获取到同一个会话的句柄。我们称这个概念为线程本地存储,意思是,使用一个特殊的对象,它将为每个应用程序线程维护一个独立的对象。Python 通过 threading.local() 构造提供了这个功能。scoped_session 对象默认使用此对象作为存储,以便为所有调用 scoped_session 注册表的人维护一个单一的 Session,但仅在单个线程范围内。在不同线程中调用注册表的调用者会获取一个仅限于该其他线程的 Session 实例。

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

scoped_session.remove() 方法始终会删除与该线程关联的当前 Session(如果有的话)。然而,threading.local() 对象的一个优点是,如果应用程序线程本身结束,那么该线程的“存储”也会被垃圾回收。因此,在一个产生并销毁线程的应用程序中使用线程局部范围实际上是“安全”的,而不需要调用 scoped_session.remove()。然而,事务本身的范围,即通过 Session.commit()Session.rollback() 结束它们,通常仍然是必须在适当的时候明确安排的东西,除非应用程序实际上将线程的寿命与事务的寿命绑定在一起。## 使用线程局部范围与 Web 应用程序

如在何时构建会话,何时提交它,何时关闭它?一节中所讨论的,一个 web 应用程序是围绕着网络请求的概念构建的,并且将这样的应用程序与 Session 集成通常意味着将 Session 与该请求相关联。事实证明,大多数 Python web 框架,特别是异步框架 Twisted 和 Tornado 之类的著名例外,以简单的方式使用线程,使得一个特定的网络请求在一个单独的工作线程的范围内接收、处理和完成。当请求结束时,工作线程被释放到一个工作线程池中,在那里它可以处理另一个请求。

这种简单的网络请求和线程的对应关系意味着,将 Session 关联到一个线程意味着它也与在该线程内运行的网络请求关联,反之亦然,前提是 Session 只在网络请求开始后创建并在网络请求结束前被销毁。因此,将 scoped_session 用作将 Session 与 web 应用程序集成的快速方法是一种常见做法。下面的顺序图说明了这个流程:

代码语言:javascript
复制
Web Server          Web Framework        SQLAlchemy ORM Code
--------------      --------------       ------------------------------
startup        ->   Web framework        # Session registry is established
                    initializes          Session = scoped_session(sessionmaker())

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

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

                                         Session.add(some_object) # ...

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

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

                    sends output      <-
outgoing web    <-
response

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

  1. 在 Web 应用程序首次启动时创建一个单一的 scoped_session 注册表,确保此对象可被应用程序的其余部分访问。
  2. 确保在 Web 请求结束时调用 scoped_session.remove(),通常是通过与 Web 框架的事件系统集成来建立“请求结束时”事件。

如前所述,上述模式只是整合 Session 到 Web 框架的一种潜在方式,特别是假定Web 框架将 Web 请求与应用线程关联。然而,强烈建议使用 Web 框架本身提供的集成工具,如果有的话,而不是 scoped_session

特别是,虽然使用线程本地存储很方便,但最好将 Session 直接与请求关联,而不是与当前线程关联。下一节关于自定义范围详细介绍了一种更高级的配置,可以将 scoped_session 的使用与直接基于请求的范围,或任何类型的范围结合起来。

使用自定义创建的范围

scoped_session 对象的默认行为“线程本地”范围只是如何“范围” Session 的许多选项之一。可以根据任何现有的“我们正在处理的当前事物”的系统来定义自定义范围。

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

代码语言:javascript
复制
from my_web_framework import get_current_request, on_request_end
from sqlalchemy.orm import scoped_session, sessionmaker

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

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

在上述情况中,我们以通常的方式实例化scoped_session,唯一的区别是我们将请求返回函数作为“scopefunc”传递。这指示scoped_session在每次调用注册表返回当前Session时使用此函数生成字典键。在这种情况下,我们特别需要确保实现可靠的“删除”系统,因为否则此字典不会自行管理。

上下文会话 API

对象名称

描述

QueryPropertyDescriptor

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

scoped_session

提供Session对象的作用域管理。

ScopedRegistry

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

ThreadLocalRegistry

使用threading.local()变量进行存储的ScopedRegistry。

代码语言:javascript
复制
class sqlalchemy.orm.scoped_session

提供Session对象的作用域管理。

参见上下文/线程本地会话教程。

注意

在使用异步 I/O (asyncio)时,应该使用与scoped_session类异步兼容的async_scoped_session类。

成员

call(), init(), add(), add_all(), autoflush, begin(), begin_nested(), bind, bulk_insert_mappings(), bulk_save_objects(), bulk_update_mappings(), close(), close_all(), commit(), configure(), connection(), delete(), deleted, dirty, execute(), expire(), expire_all(), expunge(), expunge_all(), flush(), get(), get_bind(), get_one(), identity_key(), identity_map, info, is_active, is_modified(), merge(), new, no_autoflush, object_session(), query(), query_property(), refresh(), remove(), reset(), rollback(), scalar(), scalars(), session_factory

类签名

sqlalchemy.orm.scoped_session (typing.Generic)

代码语言:javascript
复制
method __call__(**kw: Any) → _S

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

参数:

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

代码语言:javascript
复制
method __init__(session_factory: sessionmaker[_S], scopefunc: Callable[[], Any] | None = None)

构建一个新的scoped_session

参数:

  • session_factory – 一个用于创建新的Session实例的工厂。通常情况下,但不一定,这是一个sessionmaker的实例。
  • scopefunc – 可选函数,定义当前范围。如果未传递,scoped_session对象假定“线程本地”范围,并将使用 Python 的threading.local()来维护当前Session。如果传递了函数,该函数应返回一个可散列的令牌;此令牌将用作字典中的键,以便存储和检索当前Session
代码语言:javascript
复制
method add(instance: object, _warn: bool = True) → None

将一个对象放入此Session

代表scoped_session类的Session类的代理。

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

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

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

另请参阅

Session.add_all()

添加新项目或现有项目 - 在使用会话基础知识中

代码语言:javascript
复制
method add_all(instances: Iterable[object]) → None

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

代表scoped_session类的Session类的代理。

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

另请参阅

Session.add()

添加新项目或现有项目 - 在使用会话基础知识中

代码语言:javascript
复制
attribute autoflush

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

代码语言:javascript
复制
method begin(nested: bool = False) → SessionTransaction

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

代表scoped_session类的Session类的代理。

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

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

参数:

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

返回:

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

另请参阅

自动开始

管理事务

Session.begin_nested()

代码语言:javascript
复制
method begin_nested() → SessionTransaction

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

代理 scoped_session 类的 Session 类。

目标数据库及其关联的驱动程序必须支持 SQL SAVEPOINT,该方法才能正确运行。

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

返回:

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

另请参阅

使用 SAVEPOINT

Serializable isolation / Savepoints / Transactional DDL - 为了使 SAVEPOINT 正确工作,SQLite 驱动程序需要特殊的解决方法。对于 asyncio 用例,请参阅 Serializable isolation / Savepoints / Transactional DDL(asyncio 版本) 部分。

代码语言:javascript
复制
attribute bind

代理 scoped_session 类的 Session.bind 属性。

代码语言:javascript
复制
method bulk_insert_mappings(mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]], return_defaults: bool = False, render_nulls: bool = False) → None

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

代理 scoped_session 类的 Session 类。

传统功能

此方法是 SQLAlchemy 2.0 系列的传统功能。对于现代批量插入和更新,请参阅 ORM 批量 INSERT 语句 和 ORM 根据主键批量更新。2.0 API 与此方法共享实现细节,并添加了新功能。

参数:

  • mapper - 一个映射类,或者实际的 Mapper 对象,表示映射列表中表示的单个对象类型。
  • mappings – 一个字典序列,每个字典包含要插入的映射行的状态,以映射类上的属性名称表示。如果映射引用多个表,例如联合继承映射,每个字典必须包含要填充到所有表中的所有键。
  • return_defaults – 当设置为 True 时,将更改 INSERT 过程以确保获取新生成的主键值。通常设置此参数的原因是启用联合表继承映射的批量插入。 注意 对于不支持 RETURNING 的后端,Session.bulk_insert_mappings.return_defaults 参数可以显著降低性能,因为无法批量处理 INSERT 语句。请参阅 “插入多个值”行为的 INSERT 语句 了解哪些后端会受到影响的背景信息。
  • render_nulls – 当设置为 True 时,None 的值将导致 NULL 值包含在 INSERT 语句中,而不是将列从 INSERT 中省略。这允许要 INSERT 的所有行具有相同的列集,从而允许将所有行批量发送到 DBAPI。通常,包含与上一行不同的 NULL 值组合的每个列集必须省略 INSERT 语句中的一系列不同列,这意味着必须将其作为单独的语句发出。通过传递此标志,可以确保将所有行的完整集合批量处理到一个批次中;但是,成本是将被省略的列调用的服务器端默认值将被跳过,因此必须确保这些值不是必需的。 警告 当设置此标志时,不会为那些以 NULL 插入的列调用服务器端默认 SQL 值;NULL 值将被明确发送。必须小心确保整个操作不需要调用服务器端默认函数。

请参阅

ORM 启用的 INSERT、UPDATE 和 DELETE 语句

Session.bulk_save_objects()

Session.bulk_update_mappings()

代码语言:javascript
复制
method bulk_save_objects(objects: Iterable[object], return_defaults: bool = False, update_changed_only: bool = True, preserve_order: bool = True) → None

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

代理Session类,代表scoped_session类。

旧特性

此方法作为 SQLAlchemy 2.0 系列的传统功能。有关现代批量 INSERT 和 UPDATE,请参见 ORM 批量 INSERT 语句和 ORM 批量按主键 UPDATE 部分。

对于一般的 ORM 映射对象的 INSERT 和 UPDATE,请优先使用标准的工作单元数据管理模式,在 SQLAlchemy 统一教程的 ORM 数据操作中引入。SQLAlchemy 2.0 现在使用现代方言的“Insert Many Values”行为用于 INSERT 语句,解决了以前批量 INSERT 速度慢的问题。

参数:

  • objects – 一个映射对象实例的序列。映射对象按原样持久化,并且在之后Session相关联。 对于每个对象,该对象是作为 INSERT 还是 UPDATE 发送取决于传统操作中Session使用的相同规则;如果对象具有InstanceState.key属性设置,则假定对象为“分离”,并将导致 UPDATE。否则,使用 INSERT。 在 UPDATE 的情况下,语句根据已更改的属性分组,因此将成为每个 SET 子句的主题。如果update_changed_only为 False,则将应用每个对象中存在的所有属性到 UPDATE 语句中,这可能有助于将语句分组到更大的 executemany()中,并且还将减少检查属性历史记录的开销。
  • return_defaults – 当为 True 时,将缺少生成默认值的值的行插入“一次”,以便主键值可用。特别是,这将允许联合继承和其他多表映射正确插入,而无需提前提供主键值;但是,Session.bulk_save_objects.return_defaults 大大降低了该方法的性能收益。强烈建议请使用标准的Session.add_all()方法。
  • update_changed_only – 当为 True 时,基于每个状态中已记录更改的属性渲染 UPDATE 语句。当为 False 时,除主键属性外,将所有存在的属性渲染到 SET 子句中。
  • preserve_order - 当为 True 时,插入和更新的顺序与给定对象的顺序完全匹配。当为 False 时,常见类型的对象被分组为插入和更新,以便提供更多的批处理机会。

另请参阅

ORM-Enabled INSERT、UPDATE 和 DELETE 语句

Session.bulk_insert_mappings()

Session.bulk_update_mappings()

代码语言:javascript
复制
method bulk_update_mappings(mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]]) → None

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

代理了scoped_session类的Session类。

传统功能

作为 SQLAlchemy 2.0 系列的一个传统功能。有关现代批量 INSERT 和 UPDATE,请参阅 ORM 批量 INSERT 语句和 ORM 通过主键进行批量 UPDATE 部分。2.0 API 与此方法共享实现细节,并添加了新功能。

参数:

  • mapper - 一个映射类,或者表示映射列表中所表示的单一对象的实际Mapper对象。
  • mappings - 一系列字典,每个字典包含要更新的映射行的状态,以映射类上的属性名称为准。如果映射涉及多个表,比如联接继承映射,则每个字典可能包含对所有表对应的键。所有这些已存在且不是主键的键都将应用于 UPDATE 语句的 SET 子句;所需的主键值将应用于 WHERE 子句。

另请参阅

ORM-Enabled INSERT、UPDATE 和 DELETE 语句

Session.bulk_insert_mappings()

Session.bulk_save_objects()

代码语言:javascript
复制
method close() → None

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

代理了scoped_session类的Session类。

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

提示

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

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

从版本 1.4 开始更改:Session.close()方法不会立即创建新的SessionTransaction对象;只有在再次为数据库操作使用Session时才会创建新的SessionTransaction

另请参见

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

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

代码语言:javascript
复制
classmethod close_all() → None

关闭所有内存中的会话。

代理scoped_session类的Session类。

自 1.3 版本起已弃用:Session.close_all() 方法已弃用,将在将来的版本中删除。请参考close_all_sessions()

代码语言:javascript
复制
method commit() → None

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

代理了scoped_session类的Session类。

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

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

最外层数据库事务会无条件提交,自动释放任何正在进行的 SAVEPOINT。

请参阅。

提交。

管理事务。

在使用 AsyncSession 时避免隐式 IO。

代码语言:javascript
复制
method configure(**kwargs: Any) → None

重新配置由此scoped_session使用的sessionmaker

查看sessionmaker.configure()

代码语言:javascript
复制
method connection(bind_arguments: _BindArguments | None = None, execution_options: CoreExecuteOptionsParameter | None = None) → Connection

返回一个对应于此Session对象的事务状态的Connection对象。

代理了scoped_session类的Session类。

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

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

参数:

  • bind_arguments – 绑定参数字典。可能包括“mapper”、“bind”、“clause”、“其他传递给Session.get_bind()的自定义参数。
  • execution_options – 将传递给Connection.execution_options()的执行选项字典,仅在首次获取连接时。如果连接已经存在于Session中,则会发出警告并忽略参数。 另请参阅 设置事务隔离级别 / DBAPI AUTOCOMMIT
代码语言:javascript
复制
method delete(instance: object) → None

将实例标记为已删除。

代表scoped_session类为Session类代理。

当传递的对象被假定为持久的或分离的时,调用该方法后,对象将保持在持久状态,直到下一次刷新进行。在此期间,该对象还将成为Session.deleted集合的成员。

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

另请参阅

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

代码语言:javascript
复制
attribute deleted

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

代表scoped_session类为Session类进行了代理。

代码语言:javascript
复制
attribute dirty

被视为脏的所有持久实例的集合。

代表scoped_session类为Session类进行了代理。

例如:

代码语言:javascript
复制
some_mapped_object in session.dirty

实例在被修改但未被删除时被视为脏。

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

要检查实例是否对其属性有可操作的净变化,请使用Session.is_modified()方法。

代码语言:javascript
复制
method execute(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, _parent_execute_state: Any | None = None, _add_event: Any | None = None) → Result[Any]

执行 SQL 表达式构造。

代表scoped_session类为Session类进行了代理。

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

例如:

代码语言:javascript
复制
from sqlalchemy import select
result = session.execute(
    select(User).where(User.id == 5)
)

Session.execute()的 API 合同类似于Connection.execute(),2.0 风格版本的Connection

从版本 1.4 开始变更:当使用 2.0 风格 ORM 使用时,Session.execute()方法现在是 ORM 语句执行的主要点。

参数:

  • statement – 可执行的语句(即Executable表达式,如select())。
  • params – 可选字典或字典列表,其中包含绑定的参数值。如果是单个字典,则执行单行操作;如果是字典列表,则将调用“executemany”。每个字典中的键必须对应于语句中存在的参数名称。
  • execution_options – 可选的执行选项字典,将与语句执行相关联。此字典可以提供Connection.execution_options()接受的选项子集,并且还可以提供只在 ORM 上下文中理解的其他选项。 另请参阅 ORM 执行选项 - ORM 特定的执行选项
  • bind_arguments – 用于确定绑定的其他参数的字典。可能包括“mapper”,“bind”或其他自定义参数。此字典的内容传递给Session.get_bind()方法。

返回:

一个Result对象。

代码语言:javascript
复制
method expire(instance: object, attribute_names: Iterable[str] | None = None) → None

将实例的属性过期。

代表scoped_session类的Session类的代理。

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

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

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

参数:

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

另请参阅

刷新 / 过期 - 入门材料

Session.expire()

Session.refresh()

Query.populate_existing()

代码语言:javascript
复制
method expire_all() → None

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

代表 scoped_session 类,为 Session 类提供代理。

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

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

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

另请参阅

刷新 / 过期 - 入门材料

Session.expire()

Session.refresh()

Query.populate_existing()

代码语言:javascript
复制
method expunge(instance: object) → None

从此 Session 中移除实例。

代表 scoped_session 类,为 Session 类提供代理。

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

代码语言:javascript
复制
method expunge_all() → None

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

代表 scoped_session 类,为 Session 类提供代理。

这相当于在此 Session 中对所有对象调用 expunge(obj)

代码语言:javascript
复制
method flush(objects: Sequence[Any] | None = None) → None

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

代表Session类的scoped_session类。

将所有待处理的对象创建、删除和修改写入数据库,作为 INSERTs、DELETEs、UPDATEs 等。操作会自动按照会话的工作单元依赖解析器进行排序。

数据库操作将在当前事务上下文中发出,并且不会影响事务的状态,除非发生错误,在这种情况下,整个事务都将回滚。您可以在事务中随意刷新()以将更改从 Python 移动到数据库的事务缓冲区。

参数:

objects

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

此功能适用于极为狭窄的一组使用案例,其中可能需要在完全刷新()发生之前对特定对象进行操作。不适用于常规用途。

代码语言:javascript
复制
method get(entity: _EntityBindKey[_O], ident: _PKIdentityArgument, *, options: Sequence[ORMOption] | None = None, populate_existing: bool = False, with_for_update: ForUpdateParameter = None, identity_token: Any | None = None, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None) → _O | None

返回基于给定主键标识符的实例,如果找不到则返回None

代表Session类的scoped_session类。

例如:

代码语言:javascript
复制
my_user = session.get(User, 5)

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

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

从版本 1.4 开始:添加了Session.get(),它已从现在的遗留Query.get()方法中移动。

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

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

参数:

entity – 指示要加载的实体类型的映射类或Mapper

ident

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

对于单列主键,标量调用形式通常是最方便的。如果一行的主键是值“5”,调用看起来像:

代码语言:javascript
复制
my_object = session.get(SomeClass, 5)

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

代码语言:javascript
复制
my_object = session.get(SomeClass, (5, 10))

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

代码语言:javascript
复制
my_object = session.get(SomeClass, {"id": 5, "version_id": 10})

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

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

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

execution_options

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

从版本 1.4.29 开始新增。

另请参阅

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

bind_arguments

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

返回:

对象实例,或 None

代码语言:javascript
复制
method get_bind(mapper: _EntityBindKey[_O] | None = None, *, clause: ClauseElement | None = None, bind: _SessionBind | None = None, _sa_skip_events: bool | None = None, _sa_skip_for_implicit_returning: bool = False, **kw: Any) → Engine | Connection

返回此Session绑定的“bind”。

代理为Session类,代表scoped_session类。

“bind”通常是 Engine 的实例,除非 Session 已经被明确地直接绑定到 Connection 的情况除外。

对于多重绑定或未绑定的 Session,使用 mapperclause 参数来确定要返回的适当绑定。

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

解析顺序为:

  1. 如果给定了 mapper 并且 Session.binds 存在,则根据首先使用的 mapper,然后使用的 mapped class,然后使用 mapped class 的 __mro__ 中存在的任何基类来定位绑定,从更具体的超类到更一般的超类。
  2. 如果给定了 clause 并且存在 Session.binds,则根据 Session.binds 中存在的给定 clause 中的 Table 对象来定位绑定。
  3. 如果存在 Session.binds,则返回该绑定。
  4. 如果给定了 clause,则尝试返回与 clause 最终关联的元数据的绑定。
  5. 如果给定了 mapper,则尝试返回与 mapper 映射到的 Table 或其他可选择的元数据最终关联的绑定。
  6. 如果找不到绑定,则会引发 UnboundExecutionError

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

参数:

  • mapper – 可选的映射类或相应的Mapper实例。绑定可以首先通过查看与此Session相关联的“绑定”映射来派生自Mapper,其次是通过查看与Mapper映射到的Table相关联的MetaData来派生绑定。
  • clause – 一个ClauseElement(即select()text()等)。如果未提供mapper参数或无法生成绑定,则将搜索给定的表达式构造以获取绑定元素,通常是与绑定的MetaData相关联的Table

另请参阅

分区策略(例如每个会话的多个数据库后端)

Session.binds

Session.bind_mapper()

Session.bind_table()

代码语言:javascript
复制
method get_one(entity: _EntityBindKey[_O], ident: _PKIdentityArgument, *, options: Sequence[ORMOption] | None = None, populate_existing: bool = False, with_for_update: ForUpdateParameter = None, identity_token: Any | None = None, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None) → _O

基于给定的主键标识符返回一个实例,如果找不到则引发异常。

代表scoped_session类的Session类的代理。

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

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

2.0.22 版中的新功能。

返回:

对象实例。

另请参阅

Session.get() - 相应的方法,用于

如果找不到提供的主键的行,则返回None

代码语言:javascript
复制
classmethod identity_key(class_: Type[Any] | None = None, ident: Any | Tuple[Any, ...] = None, *, instance: Any | None = None, row: Row[Any] | RowMapping | None = None, identity_token: Any | None = None) → _IdentityKeyType[Any]

返回一个标识键。

代表scoped_session类的Session类的代理。

这是identity_key()的别名。

代码语言:javascript
复制
attribute identity_map

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

代码语言:javascript
复制
attribute info

用户可修改的字典。

代表scoped_session类的Session类的代理。

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

代码语言:javascript
复制
attribute is_active

如果此Session不处于“部分回滚”状态,则返回 True。

代表scoped_session类的Session类的代理。

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

“部分回滚”状态通常表示Session的刷新过程失败,并且必须发出Session.rollback()方法以完全回滚事务。

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

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

另请参阅

“由于刷新期间先前的异常,此会话的事务已回滚。”(或类似)

Session.in_transaction()

代码语言:javascript
复制
method is_modified(instance: object, include_collections: bool = True) → bool

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

代理scoped_session类为Session类。

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

这实际上是对在Session.dirty集合中检查给定实例的更昂贵且准确的版本;会执行每个属性净“脏”状态的完整测试。

例如:

代码语言:javascript
复制
return session.is_modified(someobject)

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

  • Session.dirty集合中存在的实例在使用此方法进行测试时可能报告False。这是因为对象可能已经通过属性变异接收到更改事件,从而将其放置在Session.dirty中,但最终状态与从数据库加载的状态相同,在此处没有净变化。
  • 当应用新值时,如果标量属性未加载或已过期,则可能未记录先前设置的值 - 在这些情况下,即使最终对其数据库值没有净变化,也假定属性已更改。大多数情况下,SQLAlchemy 在设置事件发生时不需要“旧”值,因此如果旧值不存在,则会跳过 SQL 调用的开销,这基于以下假设:标量值通常需要更新,在那些几种情况中不需要,平均而言比发出防御性 SELECT 要便宜。 只有在属性容器的active_history标志设置为True时,才会无条件地在设置时获取“旧”值。通常为主键属性和不是简单多对一的标量对象引用设置此标志。要为任意映射列设置此标志,请使用带有column_property()active_history参数。

参数:

  • instance – 要测试的映射实例的待处理更改。
  • include_collections – 表示是否应该包含多值集合在操作中。将其设置为False是一种仅检测基于本地列的属性(即标量列或多对一外键)的方法,这些属性在刷新时会导致此实例的更新。
代码语言:javascript
复制
method merge(instance: _O, *, load: bool = True, options: Sequence[ORMOption] | None = None) → _O

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

代理为scoped_session类代表Session类。

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

此操作如果关联映射使用cascade="merge",将级联到关联的实例。

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

参数:

  • instance – 要合并的实例。
  • load – 布尔值,当为 False 时,merge() 切换到“高性能”模式,导致它放弃发出历史事件以及所有数据库访问。此标志用于诸如从二级缓存传输对象图到Session,或将刚加载的对象传输到工作线程或进程拥有的Session中而无需重新查询数据库的情况。 load=False 的用例添加了一个警告,即给定对象必须处于“干净”状态,即没有要刷新的挂起更改 - 即使传入对象与任何Session都分离。这样,当合并操作填充本地属性并级联到相关对象和集合时,值可以“盖章”到目标对象上,而不会生成任何历史或属性事件,并且不需要将传入数据与可能未加载的任何现有相关对象或集合进行协调。load=False 的结果对象始终以“干净”方式生成,因此只有给定对象也应该“干净”,否则这表明方法的误用。
  • options – 可选的加载器选项序列,将在合并操作从数据库加载现有对象的版本时应用于Session.get()方法。 1.4.24 版本中新增。

另请参阅

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

代码语言:javascript
复制
attribute new

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

代表scoped_session类的Session类的代理。

代码语言:javascript
复制
attribute no_autoflush

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

代表scoped_session类的Session类的代理。

例如:

代码语言:javascript
复制
with session.no_autoflush:

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

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

代码语言:javascript
复制
classmethod object_session(instance: object) → Session | None

返回一个对象所属的Session

代表scoped_session类的Session类的代理。

这是object_session()的别名。

代码语言:javascript
复制
method query(*entities: _ColumnsClauseArgument[Any], **kwargs: Any) → Query[Any]

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

代表scoped_session类的Session类的代理。

注意Query对象在 SQLAlchemy 2.0 中已被废弃;现在使用select()构造 ORM 查询。

另请参阅

SQLAlchemy 统一教程

ORM 查询指南

旧版查询 API - 旧版 API 文档

代码语言:javascript
复制
method query_property(query_cls: Type[Query[_T]] | None = None) → QueryPropertyDescriptor

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

旧版特性

scoped_session.query_property() 访问器专门针对传统的 Query 对象,不被视为 2.0-style ORM 使用的一部分。

例如:

代码语言:javascript
复制
from sqlalchemy.orm import QueryPropertyDescriptor
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

Session = scoped_session(sessionmaker())

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

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

默认情况下,通过会话配置的查询类产生会话的实例。要覆盖并使用自定义实现,请提供一个 query_cls 可调用对象。将使用类的映射器作为位置参数和会话关键字参数调用可调用对象。

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

代码语言:javascript
复制
method refresh(instance: object, attribute_names: Iterable[str] | None = None, with_for_update: ForUpdateParameter = None) → None

对给定实例的过期和刷新属性。

代理为 scoped_session 类代表 Session 类。

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

如果对象已经急加载了,那么 relationship() 导向的属性也将立即加载,并使用它们最初加载的急加载策略。

新版本 1.4 中:- Session.refresh() 方法现在也可以刷新急加载的属性。

如果惰性加载的关系不在 Session.refresh.attribute_names 中命名,则它们将保持为“惰性加载”属性,并且不会隐式刷新。

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

提示

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

注意,高度隔离的事务将返回在同一事务中先前读取的相同值,而不管该事务外部数据库状态的变化如何。刷新属性通常只在事务开始时有意义,此时数据库行尚未被访问。

参数:

  • attribute_names – 可选。一个字符串属性名称的可迭代集合,指示要刷新的属性的子集。
  • with_for_update – 可选布尔值 True,表示应该使用 FOR UPDATE,或者可以是一个包含标志的字典,指示要在 SELECT 中使用一组更具体的 FOR UPDATE 标志;标志应该与 Query.with_for_update() 的参数匹配。覆盖 Session.refresh.lockmode 参数。

另请参阅

Refreshing / Expiring - 入门材料

Session.expire()

Session.expire_all()

Populate Existing - 允许任何 ORM 查询按照正常加载的方式刷新对象。

代码语言:javascript
复制
method remove() → None

如果存在,释放当前 Session

这首先会在当前 Session 上调用 Session.close() 方法,释放仍在持有的任何现有的事务/连接资源;具体地,会回滚事务。然后丢弃 Session。在同一作用域内的下一次使用时,scoped_session 将生成一个新的 Session 对象。

代码语言:javascript
复制
method reset() → None

结束此 Session 使用的事务资源和 ORM 对象,将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会将会…

代理Session类,代表scoped_session类。

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

新版本 2.0.22 中的内容。

另请参阅

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

Session.close() - 当参数Session.close_resets_only设置为False时,类似的方法还会阻止对会话的重新使用。

代码语言:javascript
复制
method rollback() → None

回滚当前进行中的事务。

代理Session类,代表scoped_session类。

如果没有事务正在进行,则此方法将被忽略。

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

另请参阅

回滚

管理事务

代码语言:javascript
复制
method scalar(statement: Executable, params: _CoreSingleExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) → Any

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

代理Session类,代表scoped_session类。

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

代码语言:javascript
复制
method scalars(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) → ScalarResult[Any]

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

代理Session类,代表scoped_session类。

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

返回:

一个 ScalarResult 对象

新特性在版本 1.4.24 中添加:增加了 Session.scalars()

新特性在版本 1.4.26 中添加:增加了scoped_session.scalars()

另见

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

代码语言:javascript
复制
attribute session_factory: sessionmaker[_S]

提供给 __init__ 的 session_factory 存储在这个属性中,以后可以访问。当需要一个新的非范围 Session 时,这可能会很有用。

代码语言:javascript
复制
class sqlalchemy.util.ScopedRegistry

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

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

参数:

  • createfunc – 一个可调用的函数,返回要放置在注册表中的新对象
  • scopefunc – 一个可调用的函数,它将返回一个用于存储/检索对象的键。

成员

init(), clear(), has(), set()

类签名

sqlalchemy.util.ScopedRegistry (typing.Generic)

代码语言:javascript
复制
method __init__(createfunc: Callable[[], _T], scopefunc: Callable[[], Any])

构建一个新的 ScopedRegistry

参数:

  • createfunc – 一个创建函数,如果当前范围中不存在,则会生成一个新值。
  • scopefunc – 返回表示当前范围的可哈希令牌的函数(例如,当前线程标识符)。
代码语言:javascript
复制
method clear() → None

清除当前范围,如果有的话。

代码语言:javascript
复制
method has() → bool

如果对象存在于当前范围中,则返回 True。

代码语言:javascript
复制
method set(obj: _T) → None

设置当前范围的值。

代码语言:javascript
复制
class sqlalchemy.util.ThreadLocalRegistry

使用 threading.local() 变量进行存储的 ScopedRegistry

类签名

sqlalchemy.util.ThreadLocalRegistrysqlalchemy.util.ScopedRegistry

代码语言:javascript
复制
class sqlalchemy.orm.QueryPropertyDescriptor

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

新版本 2.0.5 中新增。

类签名

sqlalchemy.orm.QueryPropertyDescriptortyping_extensions.Protocol

隐式方法访问

scoped_session的工作很简单;为所有请求它的人保存一个Session。为了更透明地访问这个Sessionscoped_session还包括代理行为,意味着可以直接将注册表本身视为Session;当在这个对象上调用方法时,它们会被代理到注册表维护的基础Session上:

代码语言:javascript
复制
Session = scoped_session(some_factory)

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

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

线程本地作用域

对于熟悉多线程编程的用户来说,将任何东西表示为全局变量通常都是一个坏主意,因为这意味着全局对象将被许多线程同时访问。Session对象完全设计成以非并发方式使用,从多线程的角度来看,这意味着“一次只能在一个线程中”。因此,我们上面的scoped_session使用示例,其中同一个Session对象在多个调用之间保持不变,表明需要有一些进程存在,以确保许多线程中的多个调用实际上不会获得相同的会话句柄。我们将此概念称为线程本地存储,这意味着使用一个特殊对象,该对象将维护每个应用程序线程的独立对象。Python 通过threading.local()构造提供了这一功能。scoped_session对象默认使用此对象作为存储,以便在调用scoped_session注册表的所有调用者中维护单个Session,但仅在单个线程的范围内。在不同线程中调用注册表的调用者将获得一个针对该其他线程本地的Session实例。

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

与往常一样,scoped_session.remove()方法会删除当前与线程关联的Session(如果有的话)。然而,threading.local()对象的一个优点是,如果应用程序线程本身结束,那么该线程的“存储”也会被垃圾回收。因此,在应用程序生成和销毁线程的情况下,使用线程本地作用域实际上是“安全”的,而无需调用scoped_session.remove()。然而,事务本身的范围,即通过Session.commit()Session.rollback()结束它们,通常仍然是必须在适当时候显式安排的,除非应用程序实际上将线程的寿命与事务的寿命绑定在一起。

在 Web 应用程序中使用线程本地作用域

如在何时构建会话、何时提交以及何时关闭会话?一节中所讨论的,Web 应用程序的架构围绕着web 请求的概念展开,而将这样的应用程序与Session集成通常意味着Session将与该请求相关联。事实证明,大多数 Python Web 框架(Twisted 和 Tornado 等异步框架是显著的例外)都以简单的方式使用线程,这样一个特定的 web 请求就在一个工作线程的范围内接收、处理和完成。当请求结束时,工作线程被释放到一个工作线程池中,在那里它可以处理另一个请求。

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

代码语言:javascript
复制
Web Server          Web Framework        SQLAlchemy ORM Code
--------------      --------------       ------------------------------
startup        ->   Web framework        # Session registry is established
                    initializes          Session = scoped_session(sessionmaker())

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

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

                                         Session.add(some_object) # ...

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

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

                    sends output      <-
outgoing web    <-
response

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

  1. 当 Web 应用程序首次启动时创建单个 scoped_session 注册表,确保此对象可被应用程序的其余部分访问。
  2. 确保在 Web 请求结束时调用 scoped_session.remove(),通常通过与 Web 框架的事件系统集成以建立“请求结束时”事件来实现。

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

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

使用自定义创建的作用域

scoped_session 对象的“线程本地”作用域是“对 Session 进行作用域”多种选项之一。可以基于任何现有的获取“我们正在处理的当前事物”的系统定义自定义作用域。

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

代码语言:javascript
复制
from my_web_framework import get_current_request, on_request_end
from sqlalchemy.orm import scoped_session, sessionmaker

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

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

在上面的例子中,我们以通常的方式实例化 scoped_session,唯一不同的是我们将我们的请求返回函数作为“scopefunc”传递。这指示 scoped_session 使用此函数生成字典键,每当注册表被调用以返回当前 Session 时。在这种情况下,确保实现可靠的“移除”系统非常重要,因为否则这个字典不会自我管理。

上下文会话 API

对象名称

描述

QueryPropertyDescriptor

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

scoped_session

提供对 Session 对象的作用域管理。

ScopedRegistry

可以根据“范围”函数存储单个类的一个或多个实例的注册表。

ThreadLocalRegistry

使用 threading.local() 变量进行存储的 ScopedRegistry。

代码语言:javascript
复制
class sqlalchemy.orm.scoped_session

提供对 Session 对象的作用域管理。

请查看上下文/线程本地会话以获取教程。

注意

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

成员

call(), init(), add(), add_all(), autoflush, begin(), begin_nested(), bind, bulk_insert_mappings(), bulk_save_objects(), bulk_update_mappings(), close(), close_all(), commit(), configure(), connection(), delete(), deleted, dirty, execute(), expire(), expire_all(), expunge(), expunge_all(), flush(), get(), get_bind(), get_one(), identity_key(), identity_map, info, is_active, is_modified(), merge(), new, no_autoflush, object_session(), query(), query_property(), refresh(), remove(), reset(), rollback(), scalar(), scalars(), session_factory

类签名

sqlalchemy.orm.scoped_session (typing.Generic)

代码语言:javascript
复制
method __call__(**kw: Any) → _S

如果不存在,使用scoped_session.session_factory创建当前Session

参数:

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

代码语言:javascript
复制
method __init__(session_factory: sessionmaker[_S], scopefunc: Callable[[], Any] | None = None)

构建一个新的 scoped_session

参数:

  • session_factory – 用于创建新的 Session 实例的工厂。通常情况下,但并非一定如此,它是 sessionmaker 的实例。
  • scopefunc – 定义当前范围的可选函数。如果未传递,则 scoped_session 对象假定“线程本地”范围,并将使用 Python 的 threading.local() 来维护当前 Session。如果传递,则该函数应返回可哈希的标记;此标记将用作字典中的键,以便存储和检索当前 Session
代码语言:javascript
复制
method add(instance: object, _warn: bool = True) → None

将对象放入此 Session 中。

代表 scoped_session 类的 Session 类的代理。

当传递给 Session.add() 方法时处于 transient 状态的对象将移至 pending 状态,直到下一次刷新,此时它们将移至 persistent 状态。

当传递给 Session.add() 方法时处于 detached 状态的对象将直接移至 persistent 状态。

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

另请参见

Session.add_all()

添加新项目或现有项目 - 在使用会话基础知识中

代码语言:javascript
复制
method add_all(instances: Iterable[object]) → None

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

代理Session类,代表scoped_session类。

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

另请参阅

Session.add()

添加新项目或现有项目 - 在使用会话基础知识中

代码语言:javascript
复制
attribute autoflush

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

代码语言:javascript
复制
method begin(nested: bool = False) → SessionTransaction

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

代理Session类,代表scoped_session类。

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

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

参数:

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

返回:

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

另请参阅

自动开始

管理事务

Session.begin_nested()

代码语言:javascript
复制
method begin_nested() → SessionTransaction

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

代理scoped_session类的Session类。

目标数据库及其关联的驱动程序必须支持 SQL SAVEPOINT 才能使该方法正常运行。

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

返回:

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

另请参阅

使用 SAVEPOINT

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

代码语言:javascript
复制
attribute bind

代理scoped_session类的Session.bind属性。

代码语言:javascript
复制
method bulk_insert_mappings(mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]], return_defaults: bool = False, render_nulls: bool = False) → None

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

代理scoped_session类的Session类。

旧特性

此方法是 SQLAlchemy 2.0 系列的旧特性。对于现代批量 INSERT 和 UPDATE,请参阅 ORM 批量 INSERT 语句和 ORM 按主键批量 UPDATE 部分。2.0 API 与此方法共享实现细节,并添加了新功能。

参数:

  • mapper – 一个映射类,或者实际的Mapper对象,代表映射列表中表示的对象类型。
  • mappings – 一系列字典,每个字典包含要插入的映射行的状态,以映射类上的属性名称表示。如果映射涉及多个表,例如联接继承映射,则每个字典必须包含要填充到所有表中的所有键。
  • return_defaults – 当为 True 时,插入过程将被改变,以确保新生成的主键值将被获取。通常,此参数的理由是为了使联接表继承映射能够被批量插入。 注意 对于不支持 RETURNING 的后端,Session.bulk_insert_mappings.return_defaults 参数可能会显著降低性能,因为 INSERT 语句无法再批量处理。请参阅“插入多个值”行为的 INSERT 语句以了解受影响的后端的背景信息。
  • render_nulls – 当为 True 时,None 的值将导致 NULL 值被包含在 INSERT 语句中,而不是从 INSERT 中省略该列。这允许所有要插入的行具有相同的列集,从而允许将完整的行集批量到 DBAPI。通常,每个包含与上一行不同组合的 NULL 值的列集必须从呈现的 INSERT 语句中省略一个不同的系列列,这意味着它必须作为一个单独的语句发出。通过传递此标志,保证了整个行集将被批处理到一个批次中;但代价是,由省略列调用的服务器端默认值将被跳过,因此必须小心确保这些值不是必需的。 警告 当设置此标志时,不会调用服务器端默认的 SQL 值用于那些以 NULL 插入的列;NULL 值将显式发送。必须小心确保整个操作不需要调用任何服务器端默认函数。

另请参阅

ORM-启用的 INSERT、UPDATE 和 DELETE 语句

Session.bulk_save_objects()

Session.bulk_update_mappings()

代码语言:javascript
复制
method bulk_save_objects(objects: Iterable[object], return_defaults: bool = False, update_changed_only: bool = True, preserve_order: bool = True) → None

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

代理 Session 类,代表 scoped_session 类。

遗留特性

该方法是 SQLAlchemy 2.0 系列的传统功能。对于现代批量插入和更新,请参阅 ORM 批量插入语句和 ORM 按主键批量更新部分。

对于一般的 ORM 映射对象的 INSERT 和 UPDATE,请优先使用标准的 unit of work 数据管理模式,介绍在 SQLAlchemy 统一教程的 ORM 数据操作部分。SQLAlchemy 2.0 现在使用现代方言的“插入多个值”的行为用于 INSERT 语句,解决了以前的批量插入缓慢的问题。

参数:

  • objects – 一系列映射对象实例。映射对象按原样保留,并且之后Session关联。 对于每个对象,对象是作为 INSERT 还是 UPDATE 发送取决于Session在传统操作中使用的相同规则;如果对象具有InstanceState.key属性设置,则假定对象是“分离的”,将导致 UPDATE。否则,将使用 INSERT。 在 UPDATE 的情况下,语句根据已更改的属性分组,并且因此将成为每个 SET 子句的主题。如果 update_changed_only 设置为 False,则每个对象中存在的所有属性都将应用于 UPDATE 语句,这可能有助于将语句组合成更大的 executemany(),还将减少检查属性历史的开销。
  • return_defaults – 当设置为 True 时,缺少生成默认值的值的行,即整数主键默认值和序列,将逐个插入,以便主键值可用。特别是这将允许加入继承和其他多表映射正确插入,无需提前提供主键值;然而,Session.bulk_save_objects.return_defaults 极大地降低了该方法的整体性能。强烈建议请使用标准的Session.add_all()方法。
  • update_changed_only – 当设置为 True 时,UPDATE 语句基于每个状态中已记录更改的属性。当设置为 False 时,所有存在的属性(主键属性除外)都将进入 SET 子句。
  • preserve_order – 当为 True 时,插入和更新的顺序与对象给出的顺序完全匹配。当为 False 时,常见类型的对象被分组为插入和更新,以便更多批处理机会。

另请参阅

启用 ORM 的 INSERT、UPDATE 和 DELETE 语句

Session.bulk_insert_mappings()

Session.bulk_update_mappings()

代码语言:javascript
复制
method bulk_update_mappings(mapper: Mapper[Any], mappings: Iterable[Dict[str, Any]]) → None

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

代表 scoped_session 类的 Session 类的代理。

遗留特性

此方法是 SQLAlchemy 2.0 系列的遗留特性。对于现代批量插入和更新,请参阅 ORM 批量插入语句 和 ORM 通过主键批量更新 部分。2.0 API 共享此方法的实现细节,并添加了新功能。

参数:

  • mapper – 一个映射类,或者实际的 Mapper 对象,代表映射列表中表示的单个对象类型。
  • mappings – 一个字典序列,每个字典包含要更新的映射行的状态,以映射类上的属性名称表示。如果映射涉及多个表,例如连接继承映射,则每个字典可能包含对应于所有表的键。所有那些出现且不是主键的键都应用于 UPDATE 语句的 SET 子句;必需的主键值应用于 WHERE 子句。

另请参阅

启用 ORM 的 INSERT、UPDATE 和 DELETE 语句

Session.bulk_insert_mappings()

Session.bulk_save_objects()

代码语言:javascript
复制
method close() → None

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

代表 scoped_session 类的 Session 类的代理。

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

提示

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

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

从版本 1.4 开始:Session.close()方法不会立即创建新的SessionTransaction对象; 只有在再次为数据库操作使用Session时,才会创建新的SessionTransaction

请参阅

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

Session.reset() - 一种行为类似于带有参数Session.close_resets_only设置为Trueclose()的方法。

代码语言:javascript
复制
classmethod close_all() → None

关闭所有内存中的会话。

代表scoped_session类为Session类进行了代理。

自版本 1.3 起不推荐使用:Session.close_all()方法已弃用,并将在将来的版本中删除。请参阅close_all_sessions()

代码语言:javascript
复制
method commit() → None

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

代表scoped_session类的Session类的代理。

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

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

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

另请参见

提交

事务管理

在使用 AsyncSession 时防止隐式 IO

代码语言:javascript
复制
method configure(**kwargs: Any) → None

重新配置由此scoped_session使用的sessionmaker

参见 sessionmaker.configure()

代码语言:javascript
复制
method connection(bind_arguments: _BindArguments | None = None, execution_options: CoreExecuteOptionsParameter | None = None) → Connection

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

代表scoped_session类的Session类的代理。

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

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

参数:

  • bind_arguments – 绑定参数字典。可能包括“mapper”,“bind”,“clause”,其他传递给Session.get_bind()的自定义参数。
  • execution_options – 执行选项字典,将在首次获得连接时传递给Connection.execution_options()仅在首次获得连接时。如果连接已经存在于Session中,则发出警告并忽略参数。 另请参阅 设置事务隔离级别/DBAPI AUTOCOMMIT
代码语言:javascript
复制
method delete(instance: object) → None

将实例标记为已删除。

代表scoped_session类的Session类的代理。

当传递时,假定对象为持久或分离状态;在调用该方法之后,对象将保持持久状态,直到下一个刷新发生。在此期间,对象还将是Session.deleted集合的成员。

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

另请参阅

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

代码语言:javascript
复制
attribute deleted

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

代理了scoped_session类,代表Session类。

代码语言:javascript
复制
attribute dirty

被视为脏的所有持久实例的集合。

代理了scoped_session类,代表Session类。

例如:

代码语言:javascript
复制
some_mapped_object in session.dirty

当实例被修改但未被删除时被视为脏。

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

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

代码语言:javascript
复制
method execute(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, _parent_execute_state: Any | None = None, _add_event: Any | None = None) → Result[Any]

执行 SQL 表达式构造。

代理了scoped_session类,代表Session类。

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

例如:

代码语言:javascript
复制
from sqlalchemy import select
result = session.execute(
    select(User).where(User.id == 5)
)

Session.execute()的 API 合同与Connection.execute()类似,是Connection的 2.0 风格版本。

从版本 1.4 开始:当使用 2.0 风格 ORM 使用时,Session.execute()方法现在是 ORM 语句执行的主要点。

参数:

  • statement – 可执行的语句(即Executable表达式,如select())。
  • params – 可选的字典,或包含绑定参数值的字典列表。如果是单个字典,则执行单行;如果是字典列表,则会触发“executemany”。每个字典中的键必须与语句中存在的参数名相对应。
  • execution_options – 可选的执行选项字典,将与语句执行关联。该字典可以提供 Connection.execution_options() 接受的选项子集,并且还可以提供仅在 ORM 上下文中理解的其他选项。 另请参阅 ORM 执行选项 - ORM 特定的执行选项
  • bind_arguments – 用于确定绑定的其他参数字典。可能包括“mapper”、“bind”或其他自定义参数。此字典的内容将传递给 Session.get_bind() 方法。

返回:

一个 Result 对象。

代码语言:javascript
复制
method expire(instance: object, attribute_names: Iterable[str] | None = None) → None

使实例上的属性过期。

代表 scoped_session 类,代理 Session 类。

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

同时使Session中的所有对象过期,可使用 Session.expire_all() 方法。

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

参数:

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

另请参阅

刷新 / 过期 - 入门材料

Session.expire()

Session.refresh()

Query.populate_existing()

代码语言:javascript
复制
method expire_all() → None

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

代理了 scoped_session 类的代表 Session 类。

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

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

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

另请参阅

刷新 / 过期 - 入门材料

Session.expire()

Session.refresh()

Query.populate_existing()

代码语言:javascript
复制
method expunge(instance: object) → None

从此 Session 中删除实例。

代理了 scoped_session 类的代表 Session 类。

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

代码语言:javascript
复制
method expunge_all() → None

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

代理了 scoped_session 类的代表 Session 类。

这相当于在此 Session 中的所有对象上调用 expunge(obj)

代码语言:javascript
复制
method flush(objects: Sequence[Any] | None = None) → None

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

代表 scoped_session 类,为 Session 类代理。

将所有待处理的对象创建、删除和修改写入数据库,作为 INSERT、DELETE、UPDATE 等操作。操作会自动按照 Session 的工作单元依赖解决器进行排序。

数据库操作将在当前事务上下文中发出,并且不会影响事务的状态,除非发生错误,此时整个事务将回滚。您可以在事务中随意刷新(flush())以将更改从 Python 移动到数据库的事务缓冲区。

参数:

对象 -

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

此功能仅适用于极少数情况,特定对象可能需要在完全执行 flush() 之前进行操作。不适用于一般用途。

代码语言:javascript
复制
method get(entity: _EntityBindKey[_O], ident: _PKIdentityArgument, *, options: Sequence[ORMOption] | None = None, populate_existing: bool = False, with_for_update: ForUpdateParameter = None, identity_token: Any | None = None, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None) → _O | None

返回基于给定主键标识符的实例,如果找不到,则返回 None

代表 scoped_session 类,为 Session 类代理。

例如:

代码语言:javascript
复制
my_user = session.get(User, 5)

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

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

版本 1.4 中新增:新增 Session.get(),它从现在遗留的 Query.get() 方法中移动而来。

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

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

参数:

entity - 表示要加载的实体类型的映射类或 Mapper

ident -

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

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

代码语言:javascript
复制
my_object = session.get(SomeClass, 5)

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

代码语言:javascript
复制
my_object = session.get(SomeClass, (5, 10))

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

代码语言:javascript
复制
my_object = session.get(SomeClass, {"id": 5, "version_id": 10})

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

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

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

execution_options

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

1.4.29 版本中的新功能。

另请参阅

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

bind_arguments

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

返回:

对象实例,或None

代码语言:javascript
复制
method get_bind(mapper: _EntityBindKey[_O] | None = None, *, clause: ClauseElement | None = None, bind: _SessionBind | None = None, _sa_skip_events: bool | None = None, _sa_skip_for_implicit_returning: bool = False, **kw: Any) → Engine | Connection

返回此Session绑定的“bind”。

代理给scoped_session类的Session类。

“bind”通常是Engine的实例,除非Session已经明确地直接绑定到Connection的情况除外。

对于多重绑定或未绑定的Session,使用mapperclause参数来确定要返回的适当绑定。

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

解析顺序为:

  1. 如果提供了映射器并且Session.binds存在,则首先基于正在使用的映射器,然后基于正在使用的映射类,最后基于映射类的__mro__中存在的任何基类来定位绑定,从更具体的超类到更一般的超类。
  2. 如果提供了 clause 并且存在Session.binds,则基于Session.binds中存在的给定 clause 中的Table对象来定位绑定。
  3. 如果存在Session.binds,则返回该绑定。
  4. 如果提供了 clause,则尝试返回与最终与 clause 相关联的MetaData相关联的绑定。
  5. 如果提供了映射器,则尝试返回与映射器映射到的Table或其他可选择对象最终相关联的MetaData相关联的绑定。
  6. 无法找到绑定时,引发UnboundExecutionError

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

参数:

  • mapper – 可选的映射类或对应的Mapper实例。绑定可以首先通过查看与此Session关联的“binds”映射,其次通过查看与此Mapper映射到的Table相关联的MetaData来派生绑定。
  • clause – 一个ClauseElement(即select(), text()等)。如果不存在mapper参数或无法生成绑定,则将搜索给定的表达式构造,通常是与绑定的MetaData关联的Table

另请参见

分区策略(例如每个会话多个数据库后端)

Session.binds

Session.bind_mapper()

Session.bind_table()

代码语言:javascript
复制
method get_one(entity: _EntityBindKey[_O], ident: _PKIdentityArgument, *, options: Sequence[ORMOption] | None = None, populate_existing: bool = False, with_for_update: ForUpdateParameter = None, identity_token: Any | None = None, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None) → _O

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

代理为scoped_session类代表Session类。

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

要详细了解参数的文档,请参阅方法Session.get()

版本 2.0.22 中的新功能。

返回:

对象实例。

另请参见

Session.get() - 相同的方法,但代替

如果找不到提供的主键的行,则返回None

代码语言:javascript
复制
classmethod identity_key(class_: Type[Any] | None = None, ident: Any | Tuple[Any, ...] = None, *, instance: Any | None = None, row: Row[Any] | RowMapping | None = None, identity_token: Any | None = None) → _IdentityKeyType[Any]

返回一个身份键。

代理为scoped_session类代表Session类。

这是identity_key()的别名。

代码语言:javascript
复制
attribute identity_map

代理scoped_session类的Session.identity_map属性。

代码语言:javascript
复制
attribute info

用户可修改的字典。

代理scoped_session类的Session类。

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

代码语言:javascript
复制
attribute is_active

如果此Session不处于“部分回滚”状态,则返回 True。

代理scoped_session类的Session类。

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

“部分回滚”状态通常表示Session的刷新过程失败,并且必须发出Session.rollback()方法以完全回滚事务。

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

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

另请参阅

“由于在刷新期间发生的先前异常,此会话的事务已回滚。”(或类似)

Session.in_transaction()

代码语言:javascript
复制
method is_modified(instance: object, include_collections: bool = True) → bool

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

代表scoped_session类的Session类的代理。

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

实际上,这是一个更昂贵且更准确的版本,用于检查给定实例是否在Session.dirty集合中;对于每个属性的净“脏”状态进行了全面测试。

例如:

代码语言:javascript
复制
return session.is_modified(someobject)

此方法有一些注意事项:

  • 当使用此方法测试时,Session.dirty集合中存在的实例可能报告False。这是因为该对象可能已通过属性突变接收到更改事件,从而将其放置在Session.dirty中,但最终状态与从数据库加载的状态相同,在此处没有净更改。
  • 当新值被应用时,标量属性可能未记录先前设置的值,如果属性在新值接收时未加载或过期,则在这些情况下,即使最终没有对其数据库值进行净更改,也假定该属性发生了更改。大多数情况下,当发生设置事件时,SQLAlchemy 不需要“旧”值,因此,如果旧值不存在,则跳过 SQL 调用的开销,基于假设更新标量值通常是必要的,而在那些很少的情况下它不是,平均而言比发出防御性 SELECT 要便宜。 当属性容器的active_history标志设置为True时,将无条件获取“旧”值,仅在发生设置时。通常为主键属性和非简单多对一的标量对象引用设置此标志。要为任意映射列设置此标志,请使用column_property()中的active_history参数。

参数:

  • instance - 要测试是否存在未决更改的映射实例。
  • include_collections - 表示是否应在操作中包含多值集合。将其设置为False是一种检测仅基于本地列的属性(即标量列或多对一外键)的方法,这些属性在刷新此实例时将导致 UPDATE。
代码语言:javascript
复制
method merge(instance: _O, *, load: bool = True, options: Sequence[ORMOption] | None = None) → _O

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

代理Session类,代表scoped_session类。

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

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

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

参数:

  • instance – 要合并的实例。
  • load – 当为 False 时,merge()切换到“高性能”模式,导致它放弃发出历史事件以及所有数据库访问。此标志用于将对象图转移到来自第二级缓存的Session中,或者将刚加载的对象转移到由工作线程或进程拥有的Session中,而无需重新查询数据库。 load=False用例添加了这样的警告,即给定对象必须处于“干净”状态,即没有待刷新的更改 - 即使传入对象与任何Session都没有关联。这是为了当合并操作填充本地属性并级联到相关对象和集合时,值可以按原样“盖印”到目标对象上,而不生成任何历史或属性事件,并且无需将传入数据与可能未加载的任何现有相关对象或集合进行协调。来自load=False的结果对象始终作为“干净”生成,因此只有给定的对象也应该“干净”,否则这表明方法的错误使用。
  • options – 在合并操作加载对象的现有版本时,将应用到 Session.get() 方法的可选加载器选项序列。 新版本 1.4.24 中新增。

另请参阅

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

代码语言:javascript
复制
attribute new

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

代表 scoped_session 类而为 Session 类代理。

代码语言:javascript
复制
attribute no_autoflush

返回一个上下文管理器,用于禁用自动提交。

代表 scoped_session 类而为 Session 类代理。

例如:

代码语言:javascript
复制
with session.no_autoflush:

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

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

代码语言:javascript
复制
classmethod object_session(instance: object) → Session | None

返回对象所属的 Session

代表 scoped_session 类而为 Session 类代理。

这是 object_session() 的别名。

代码语言:javascript
复制
method query(*entities: _ColumnsClauseArgument[Any], **kwargs: Any) → Query[Any]

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

代表 scoped_session 类而为 Session 类代理。

请注意,Query 对象在 SQLAlchemy 2.0 中已被标记为遗留;现在使用 select() 构造 ORM 查询。

另请参阅

SQLAlchemy 统一教程

ORM 查询指南

遗留查询 API - 遗留 API 文档

代码语言:javascript
复制
method query_property(query_cls: Type[Query[_T]] | None = None) → QueryPropertyDescriptor

返回一个类属性,当调用时,该属性会针对类和当前 Session 生成一个遗留 Query 对象。

遗留特性

scoped_session.query_property() 访问器是特定于传统的 Query 对象的,不被视为 2.0 风格 ORM 使用的一部分。

例如:

代码语言:javascript
复制
from sqlalchemy.orm import QueryPropertyDescriptor
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker

Session = scoped_session(sessionmaker())

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

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

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

类上放置的查询属性数量没有限制。

代码语言:javascript
复制
method refresh(instance: object, attribute_names: Iterable[str] | None = None, with_for_update: ForUpdateParameter = None) → None

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

代理scoped_session 类的 Session 类。

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

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

自 1.4 版开始:- Session.refresh() 方法也可以刷新急切加载的属性。

如果通常使用select(或“延迟”)加载器策略加载的 relationship() 导向属性也将加载,如果它们在 attribute_names 集合中明确命名,则使用 immediate 加载器策略发出 SELECT 语句加载该属性。如果惰性加载的关系未在 Session.refresh.attribute_names 中命名,则它们将保持为“惰性加载”属性,不会被隐式刷新。

从版本 2.0.4 开始更改:Session.refresh() 方法现在将刷新 relationship() 导向属性的惰性加载属性,对于那些在 Session.refresh.attribute_names 集合中明确命名的属性。

提示

虽然 Session.refresh() 方法能够刷新列和关系导向属性,但其主要重点是刷新单个实例上的本地列导向属性。对于更开放式的“刷新”功能,包括能够同时刷新多个对象的属性,并对关系加载器策略有明确控制的功能,请使用填充现有对象功能。

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

参数:

  • attribute_names – 可选。一个包含字符串属性名称的可迭代集合,指示要刷新的属性子集。
  • with_for_update – 可选布尔值 True 表示应该使用 FOR UPDATE,或者可以是一个包含标志的字典,指示应该使用更具体的 FOR UPDATE 标志集合进行 SELECT;标志应该与 Query.with_for_update() 的参数匹配。取代 Session.refresh.lockmode 参数。

另请参阅

刷新 / 过期 - 入门材料

Session.expire()

Session.expire_all()

填充现有对象 - 允许任何 ORM 查询刷新对象,就像它们通常加载的方式一样。

代码语言:javascript
复制
method remove() → None

丢弃当前Session(如果存在)。

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

代码语言:javascript
复制
method reset() → None

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

代理scoped_session类代表Session类。

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

版本 2.0.22 中的新功能。

另请参见

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

Session.close() - 当参数Session.close_resets_only设置为False时,类似的方法还会阻止重复使用 Session。

代码语言:javascript
复制
method rollback() → None

回滚当前进行中的事务。

代理scoped_session类代表Session类。

如果没有进行中的事务,则此方���是一个透传。

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

另请参见

回滚

管理事务

代码语言:javascript
复制
method scalar(statement: Executable, params: _CoreSingleExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) → Any

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

代理scoped_session类代表Session类。

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

代码语言:javascript
复制
method scalars(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) → ScalarResult[Any]

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

代理scoped_session类代表Session类。

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

返回:

一个ScalarResult对象

新版本 1.4.24 中新增:Session.scalars()

新版本 1.4.26 中新增:scoped_session.scalars()

另请参见

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

代码语言:javascript
复制
attribute session_factory: sessionmaker[_S]

提供给 init 的 session_factory 存储在此属性中,可以在以后访问。当需要新的非范围化的Session时,这可能会有用。

代码语言:javascript
复制
class sqlalchemy.util.ScopedRegistry

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

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

参数:

  • createfunc – 返回要放置在注册表中的新对象的可调用函数
  • scopefunc – 一个可调用函数,将返回一个键以存储/检索对象。

成员

init(), clear(), has(), set()

类签名

sqlalchemy.util.ScopedRegistrytyping.Generic

代码语言:javascript
复制
method __init__(createfunc: Callable[[], _T], scopefunc: Callable[[], Any])

构造一个新的ScopedRegistry

参数:

  • createfunc – 如果当前范围中不存在,则生成新值的创建函数。
  • scopefunc – 一个返回表示当前范围的可哈希令牌的函数(例如,当前线程标识符)。
代码语言:javascript
复制
method clear() → None

清除当前范围(如果有)。

代码语言:javascript
复制
method has() → bool

如果当前范围中存在对象,则返回 True。

代码语言:javascript
复制
method set(obj: _T) → None

设置当前范围的值。

代码语言:javascript
复制
class sqlalchemy.util.ThreadLocalRegistry

使用threading.local()变量进行存储的ScopedRegistry

类签名

sqlalchemy.util.ThreadLocalRegistrysqlalchemy.util.ScopedRegistry

代码语言:javascript
复制
class sqlalchemy.orm.QueryPropertyDescriptor

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

自 2.0.5 版本新增。

类签名

sqlalchemy.orm.QueryPropertyDescriptortyping_extensions.Protocol) 方法也可以刷新急切加载的属性。

如果通常使用select(或“延迟”)加载器策略加载的 relationship() 导向属性也将加载,如果它们在 attribute_names 集合中明确命名,则使用 immediate 加载器策略发出 SELECT 语句加载该属性。如果惰性加载的关系未在 Session.refresh.attribute_names 中命名,则它们将保持为“惰性加载”属性,不会被隐式刷新。

从版本 2.0.4 开始更改:Session.refresh() 方法现在将刷新 relationship() 导向属性的惰性加载属性,对于那些在 Session.refresh.attribute_names 集合中明确命名的属性。

提示

虽然 Session.refresh() 方法能够刷新列和关系导向属性,但其主要重点是刷新单个实例上的本地列导向属性。对于更开放式的“刷新”功能,包括能够同时刷新多个对象的属性,并对关系加载器策略有明确控制的功能,请使用填充现有对象功能。

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

参数:

  • attribute_names – 可选。一个包含字符串属性名称的可迭代集合,指示要刷新的属性子集。
  • with_for_update – 可选布尔值 True 表示应该使用 FOR UPDATE,或者可以是一个包含标志的字典,指示应该使用更具体的 FOR UPDATE 标志集合进行 SELECT;标志应该与 Query.with_for_update() 的参数匹配。取代 Session.refresh.lockmode 参数。

另请参阅

刷新 / 过期 - 入门材料

Session.expire()

Session.expire_all()

填充现有对象 - 允许任何 ORM 查询刷新对象,就像它们通常加载的方式一样。

代码语言:javascript
复制
method remove() → None

丢弃当前Session(如果存在)。

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

代码语言:javascript
复制
method reset() → None

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

代理scoped_session类代表Session类。

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

版本 2.0.22 中的新功能。

另请参见

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

Session.close() - 当参数Session.close_resets_only设置为False时,类似的方法还会阻止重复使用 Session。

代码语言:javascript
复制
method rollback() → None

回滚当前进行中的事务。

代理scoped_session类代表Session类。

如果没有进行中的事务,则此方���是一个透传。

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

另请参见

回滚

管理事务

代码语言:javascript
复制
method scalar(statement: Executable, params: _CoreSingleExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) → Any

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

代理scoped_session类代表Session类。

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

代码语言:javascript
复制
method scalars(statement: Executable, params: _CoreAnyExecuteParams | None = None, *, execution_options: OrmExecuteOptionsParameter = {}, bind_arguments: _BindArguments | None = None, **kw: Any) → ScalarResult[Any]

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

代理scoped_session类代表Session类。

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

返回:

一个ScalarResult对象

新版本 1.4.24 中新增:Session.scalars()

新版本 1.4.26 中新增:scoped_session.scalars()

另请参见

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

代码语言:javascript
复制
attribute session_factory: sessionmaker[_S]

提供给 init 的 session_factory 存储在此属性中,可以在以后访问。当需要新的非范围化的Session时,这可能会有用。

代码语言:javascript
复制
class sqlalchemy.util.ScopedRegistry

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

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

参数:

  • createfunc – 返回要放置在注册表中的新对象的可调用函数
  • scopefunc – 一个可调用函数,将返回一个键以存储/检索对象。

成员

init(), clear(), has(), set()

类签名

sqlalchemy.util.ScopedRegistrytyping.Generic

代码语言:javascript
复制
method __init__(createfunc: Callable[[], _T], scopefunc: Callable[[], Any])

构造一个新的ScopedRegistry

参数:

  • createfunc – 如果当前范围中不存在,则生成新值的创建函数。
  • scopefunc – 一个返回表示当前范围的可哈希令牌的函数(例如,当前线程标识符)。
代码语言:javascript
复制
method clear() → None

清除当前范围(如果有)。

代码语言:javascript
复制
method has() → bool

如果当前范围中存在对象,则返回 True。

代码语言:javascript
复制
method set(obj: _T) → None

设置当前范围的值。

代码语言:javascript
复制
class sqlalchemy.util.ThreadLocalRegistry

使用threading.local()变量进行存储的ScopedRegistry

类签名

sqlalchemy.util.ThreadLocalRegistrysqlalchemy.util.ScopedRegistry

代码语言:javascript
复制
class sqlalchemy.orm.QueryPropertyDescriptor

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

自 2.0.5 版本新增。

类签名

sqlalchemy.orm.QueryPropertyDescriptortyping_extensions.Protocol

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 附加持久化技术
    • 将 SQL 插入/更新表达式嵌入到刷新中
      • 情况 2:表中包含触发器生成的值,这些值与 RETURNING 不兼容
      • 情况 3:非主键、不支持或不需要 RETURNING 或等效功能
      • 情况 4:支持主键、RETURNING 或等效功能
      • 情况 5:主键,不支持 RETURNING 或等价的功能
      • 关于急切获取客户端调用的用于 INSERT 或 UPDATE 的 SQL 表达式的注意事项
      • 多引擎会话的事务协调
      • 自定义垂直分区
      • 水平分区
    • 使用 SQL 表达式与会话
      • 强制将 NULL 值放入具有默认值的列
        • 获取服务器生成的默认值
          • 情况 1:非主键,支持 RETURNING 或等效操作
          • 情况 2:表包含不兼容于 RETURNING 的触发器生成的值
          • 情况 3:非主键,不支持或不需要 RETURNING 或等效功能
          • 情况 4:主键,支持 RETURNING 或等效功能
          • 情况 5:不支持主键、RETURNING 或等效功能。
          • 注意事项:对于用于 INSERT 或 UPDATE 的急切提取客户端调用的 SQL 表达式
          • 情况 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 对象
          • 使用 PostgreSQL ON CONFLICT 与 RETURNING 返回 upserted ORM 对象
        • 分区策略(例如,每个会话使用多个数据库后端)
          • 简单的垂直分区
          • 为多引擎会话协调事务
          • 自定义垂直分区
          • 水平分区
          • 简单的垂直分区
          • 多引擎会话的事务协调
          • 自定义垂直分区
          • 水平分区
        • 批量操作
        • 上下文/线程本地会话
          • 隐式方法访问
            • 线程本地作用域
              • 使用自定义创建的范围
                • 上下文会话 API
                  • 隐式方法访问
                    • 线程本地作用域
                      • 在 Web 应用程序中使用线程本地作用域
                        • 使用自定义创建的作用域
                          • 上下文会话 API
                          相关产品与服务
                          数据库
                          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档