Release: 1.4.25 | Release Date: September 22, 2021

SQLAlchemy 1.4 Documentation

会话基础

会议的作用是什么?

一般来说, Session 建立与数据库的所有对话,并表示在数据库生命周期内已加载或与之关联的所有对象的“保留区域”。它提供了一个界面,在这里进行SELECT和其他查询,这些查询将返回和修改ORM映射的对象。ORM对象本身在 Session ,在名为 identity map -维护每个对象唯一副本的数据结构,其中“唯一”表示“只有一个具有特定主键的对象”。

这个 Session 以无状态的形式开始。一旦发出查询或其他对象被持久化,它将从 EngineSession ,然后在该连接上建立一个事务。在该交易保持有效之前 Session 指示提交或回滚事务。

由一个 Sessioninstrumented 这样,每当在Python程序中修改属性或集合时,都会生成一个更改事件,该事件由 Session . 无论何时查询数据库,或当事务即将提交时 Session 第一 冲洗 所有挂起的更改存储在数据库的内存中。这被称为 unit of work 模式。

当使用 Session ,考虑它维护的ORM映射对象是很有用的 代理对象 数据库行,这些行是由 Session . 为了保持对象的状态与数据库中的实际情况相匹配,有多种事件会导致对象重新访问数据库以保持同步。可以将对象从 Session 并继续使用它们,尽管这种做法有其注意事项。通常情况下,您会将分离的对象与另一个对象关联起来 Session 当您想再次使用它们时,这样它们就可以恢复它们表示数据库状态的正常任务。

使用会话的基础知识

最基本的 Session 这里介绍了使用模式。

打开和关闭会话

这个 Session 可自行建造或使用 sessionmaker 班级。它通常传递一个 Engine 作为前端连接的来源。一个典型的用法可能是:

from sqlalchemy import create_engine
from sqlalchemy.orm import Session

# an Engine, which the Session will use for connection
# resources
engine = create_engine('postgresql://scott:tiger@localhost/')

# create session and add objects
with Session(engine) as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()

上图是 Session 被实例化为 Engine 与特定数据库URL相关联。然后在Python上下文管理器中使用它(即 with: 语句),以便它在挡路结束时自动关闭;这等效于调用 Session.close() 方法。

呼唤 Session.commit() 是可选的,只有在我们完成了 Session 包括要持久化到数据库的新数据。如果我们只发出SELECT调用而不需要写入任何更改,则 Session.commit() 没必要。

构建begin/commit/rollback块

我们也可以附上 Session.commit() 调用和上下文管理器中事务的整体“框架”,用于将数据提交到数据库的情况。“框架”是指如果所有操作成功 Session.commit() 方法,但如果引发任何异常,则 Session.rollback() 方法,以便在向外传播异常之前立即回滚事务。在Python中,最基本的表达方式是使用 try: / except: / else: 块,例如:

# verbose version of what a context manager will do
with Session(engine) as session:
    session.begin()
    try:
        session.add(some_object)
        session.add(some_other_object)
    except:
        session.rollback()
        raise
    else:
        session.commit()

通过使用 SessionTransaction 返回的对象 Session.begin() 方法,它为相同的操作序列提供上下文管理器接口:

# create session and add objects
with Session(engine) as session:
    with session.begin():
      session.add(some_object)
      session.add(some_other_object)
    # inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

更简洁地说,这两个上下文可以组合在一起:

# create session and add objects
with Session(engine) as session, session.begin():
    session.add(some_object)
    session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

使用sessionmaker

目的 sessionmaker 提供一个工厂 Session 具有固定配置的对象。通常,应用程序将具有 Engine 对象在模块范围内 sessionmaker 可以提供工厂 Session 反对此引擎的对象:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources, typically in module scope
engine = create_engine('postgresql://scott:tiger@localhost/')

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
    session.add(some_object)
    session.add(some_other_object)
    session.commit()
# closes the session

这个 sessionmaker 类似于 Engine 作为功能级会话/连接的模块级工厂。因此,它也有自己的 sessionmaker.begin() 方法,类似于 Engine.begin() ,返回 Session 对象,并且还维护一个begin/commit/rollback块:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

# an Engine, which the Session will use for connection
# resources
engine = create_engine('postgresql://scott:tiger@localhost/')

# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)

# we can now construct a Session() and include begin()/commit()/rollback()
# at once
with Session.begin() as session:
    session.add(some_object)
    session.add(some_other_object)
# commits the transaction, closes the session

在上面的地方, Session 将两者都提交其事务以及 Session 将关闭,当上面的 with: 块结束。

当您编写应用程序时 sessionmaker 工厂的作用域应与 Engine 对象创建者 create_engine() ,通常在模块级或全局范围内。由于这些对象都是工厂,因此它们可以同时被任意数量的函数和线程使用。

查询(1.x样式)

这个 Session.query() 函数需要一个或多个 实体 并返回新的 Query 对象,该对象将在此会话的上下文中发出映射器查询。“实体”指的是映射类、映射类的属性或其他ORM构造,如 aliased() 结构:

# query from a class
results = session.query(User).filter_by(name='ed').all()

# query with multiple classes, returns tuples
results = session.query(User, Address).join('addresses').filter_by(name='ed').all()

# query using orm-columns, also returns tuples
results = session.query(User.name, User.fullname).all()

当ORM对象在结果中返回时,它们也存储在标识映射中。当传入数据库行的主键与已存在的对象匹配时,将返回相同的对象,并且不会重新填充已具有值的对象的那些属性。

这个 Session 自动终止事务边界上的所有实例(即当当前事务被提交或回滚时),因此对于通常隔离的事务,数据将在新事务开始时自动刷新。

这个 Query 对象在 对象关系教程(1.x API) ,并进一步记录在 查询API .

查询(2.0样式)

1.4 新版功能.

SQLAlchemy 2.0将通过直接使用 Select 对象,不需要在ORM中 Query 对象。这种操作模式现在在SQLAlchemy 1.4中可用,以支持将要迁移到2.0的应用程序。这个 Session 必须用 Session.future 标志设置为 True ;从那时起 Session.execute() 方法将通过标准返回ORM结果 Result 调用时的对象 select() 使用ORM实体的语句:

from sqlalchemy import select
from sqlalchemy.orm import Session

session = Session(engine, future=True)

# query from a class
statement = select(User).filter_by(name="ed")

# list of first element of each row (i.e. User objects)
result = session.execute(statement).scalars().all()

# query with multiple classes
statement = select(User, Address).join('addresses').filter_by(name='ed')

# list of tuples
result = session.execute(statement).all()

# query with ORM columns
statement = select(User.name, User.fullname)

# list of tuples
result = session.execute(statement).all()

需要注意的是 QueryQuery.all()Query.one() 如果只请求了一个完整的实体,则将直接返回ORM映射对象的实例 Result 对象返回者 Session.execute() 默认情况下,将始终传递行(命名元组);这样一来,针对单个或多个ORM对象、列、表等的结果可以完全相同地处理。

如果只查询了一个ORM实体,则返回的行将只有一列,由每行的ORM映射对象实例组成。若要将这些行转换为不带元组的对象实例,则 Result.scalars() 方法首先对结果应用“标量”过滤器;然后 Result 可以通过诸如 Result.all()Result.first() 等。

添加新项目或现有项目

Session.add() 用于在会话中放置实例。为了 transient (即全新的)实例,这将具有在下次刷新时为这些实例进行插入的效果。例如 persistent (即由本次会话加载),它们已经存在,不需要添加。实例是 detached (即已从会话中删除)可以使用以下方法与会话重新关联:

user1 = User(name='user1')
user2 = User(name='user2')
session.add(user1)
session.add(user2)

session.commit()     # write changes to the database

要一次向会话添加项目列表,请使用 Session.add_all() ::

session.add_all([item1, item2, item3])

这个 Session.add() 操作 级联 沿着 save-update 级联。有关更多详细信息,请参阅部分 级联 .

删除

这个 Session.delete() 方法将实例放入要标记为已删除的会话对象列表中::

# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)

# commit (or flush)
session.commit()

Session.delete() 标记要删除的对象,这将导致为每个受影响的主键发出DELETE语句。在刷新挂起的删除之前,标记为“delete”的对象将出现在 Session.deleted 收藏。删除后,将从 Session ,在提交事务后将成为永久性的。

有很多重要的行为 Session.delete() 操作,特别是如何处理与其他对象和集合的关系。在这个部分有更多的信息 级联 ,但一般规则是:

  • 与映射对象相对应的行,这些对象通过 relationship() 指令是 默认不删除 . 如果要将这些列设置为NULL,则这些列将被删除。如果列不可为null,这将导致约束冲突。

  • 要将“setnull”更改为删除相关对象的行,请使用 删除 级联 relationship() .

  • “通过”链接到表中的许多行 relationship.secondary 参数, are 当它们所引用的对象被删除时,在所有情况下都会被删除。

  • 当相关对象包含一个外键约束返回到要删除的对象,并且它们所属的相关集合当前未加载到内存中时,工作单元将发出一个SELECT以获取所有相关行,以便它们的主键值可用于在这些相关行上发出UPDATE或DELETE语句。这样,没有进一步指令的ORM将执行ON DELETE CASCADE功能,即使这是在内核上配置的 ForeignKeyConstraint 物体。

  • 这个 relationship.passive_deletes 参数可用于调整此行为并更自然地依赖于“ON DELETE CASCADE”;当设置为True时,将不再执行此SELECT操作,但是本地存在的行仍将受到显式set NULL或DELETE的约束。设置 relationship.passive_deletes"all" 将禁用 all 相关对象更新/删除。

  • 当删除标记为删除的对象时,不会自动从引用该对象的集合或对象引用中删除该对象。当 Session 已过期,则可以再次加载这些集合,以便对象不再存在。但是,最好不要使用 Session.delete() 对于这些对象,应该从其集合中删除该对象,然后 删除孤儿 应将其作为该集合移除的次要效果删除。参见章节 删除引用的对象和标量关系的注释 举个例子。

参见

删除 -描述“删除级联”,它在删除引导对象时标记要删除的相关对象。

删除孤儿 -描述“删除孤立级联”,当相关对象与其主对象取消关联时,将其标记为删除。

删除引用的对象和标量关系的注释 -重要背景 Session.delete() as涉及到在内存中刷新关系。

冲洗

Session 与默认配置一起使用时,刷新步骤几乎总是透明地完成的。具体地说,刷新发生在发出任何单个SQL语句之前,因为 Query 或A 2.0-style Session.execute() 呼叫,以及 Session.commit() 在提交事务之前调用。当 Session.begin_nested() 使用。

无论自动冲洗设置如何,都可以通过发出 Session.flush() ::

session.flush()

可以通过构造 sessionmaker 带旗 autoflush=False ::

Session = sessionmaker(autoflush=False)

此外,通过设置 autoflush 随时标记:

mysession = Session()
mysession.autoflush = False

更方便的是,可以使用 Session.no_autoflush ::

with mysession.no_autoflush:
    mysession.add(some_object)
    mysession.flush()

冲洗过程 总是 在事务中发生,即使 Session 已配置为 autocommit=True ,禁用会话的持久事务状态的设置。如果没有交易, Session.flush() 创建自己的事务并提交它。刷新期间的任何失败都将始终导致回滚任何存在的事务。如果会话不在 autocommit=True 模式,对 Session.rollback() 在刷新失败后是必需的,即使基础事务已经回滚了——这是为了使所谓的“子事务”的整体嵌套模式始终保持不变。

过期/刷新

在使用 Session 是指处理从数据库加载的对象上的状态,使它们与事务的当前状态保持同步。SQLAlchemy ORM基于一个 identity map 这样,当从SQL查询“加载”一个对象时,将有一个惟一的Python对象实例,该实例与特定的数据库标识相对应。这意味着,如果我们发出两个单独的查询,每个查询针对同一行,并返回一个映射对象,那么这两个查询将返回相同的Python对象:

>>> u1 = session.query(User).filter(id=5).first()
>>> u2 = session.query(User).filter(id=5).first()
>>> u1 is u2
True

此后,当ORM从查询中获取行时,它将 跳过属性填充 对于已加载的对象。这里的设计假设是假设事务是完全隔离的,然后在事务没有被隔离的情况下,应用程序可以根据需要采取步骤从数据库事务中刷新对象。FAQ条目 我正在用会话加载数据,但没有看到我在其他地方所做的更改。 更详细地讨论了这个概念。

将当前事务从ORM加载到内存中时,通常有三种方式将其从当前的ORM加载到内存中:

  • expire()方法 - Session.expire() 方法将删除对象的选定属性或所有属性的内容,以便下次访问时从数据库加载这些属性,例如使用 lazy loading 模式:

    session.expire(u1)
    u1.some_attribute  # <-- lazy loads from the transaction
  • refresh()方法 -密切相关的是 Session.refresh() 方法,它执行所有 Session.expire() 方法会立即发出一个或多个SQL查询,以实际刷新对象的内容:

    session.refresh(u1)  # <-- emits a SQL query
    u1.some_attribute  # <-- is refreshed from the transaction
  • the populate_existing() method -这个方法实际上是在 Query 对象AS Query.populate_existing() 并指示它应返回从数据库中的内容无条件重新填充的对象:

    u2 = session.query(User).populate_existing().filter(id=5).first()

有关刷新/过期概念的进一步讨论,请访问 刷新/过期 .

使用任意WHERE子句更新和删除

上面的部分位于 Session.flush()Session.delete() 详细说明如何根据应用程序中映射的Python对象所指向的主键标识在数据库中插入、更新和删除行。这个 Session 还可以发出带有任意WHERE子句的UPDATE和DELETE语句,同时本地刷新与这些行匹配的对象。

在中发出启用了ORM的更新 1.x style , the Query.update() 可使用的方法:

session.query(User).filter(User.name == "squidward").\
    update({"name": "spongebob"}, synchronize_session="fetch")

在上面,将针对所有匹配名称“squidward”的行发出更新,并将更新为名称“spongebo”。这个 Query.update.synchronize_session 引用“fetch”的参数表示应该通过单独的SELECT语句或通过return(如果后端数据库支持)来获取受影响的主键列表;内存中本地存在的对象将根据这些主键标识在内存中更新。

中启用ORM的更新 2.0 styleSession.execute() 与核心一起使用 Update 结构:

from sqlalchemy import update

stmt = update(User).where(User.name == "squidward").values(name="spongebob").\
    execution_options(synchronize_session="fetch")

result = session.execute(stmt)

上面, Update.execution_options() 方法可用于建立执行时间选项,如“同步会话”。

返回的结果对象是 CursorResult ;若要检索与任何UPDATE或DELETE语句匹配的行数,请使用 CursorResult.rowcount ::

num_rows_matched = result.rowcount

除了没有建立“values/set”子句外,删除操作与UPDATE相同。使用synchronizeu session时,在 Session 将标记为已删除和已删除。

启用ORM删除, 1.x style ::

session.query(User).filter(User.name == "squidward").\
    delete(synchronize_session="fetch")

启用ORM删除, 2.0 style ::

from sqlalchemy import delete

stmt = delete(User).where(User.name == "squidward").execution_options(synchronize_session="fetch")

session.execute(stmt)

选择同步策略

由于启用了ORM的1.x和2.0形式的更新和删除,下面的值 synchronize_session 支持:

  • False -不要同步会话。此选项是最有效的,并且在会话过期后非常可靠,通常在commit()或显式使用expireu all()之后发生。在过期之前,数据库中已更新或删除的对象可能仍保留在会话中,但值已过时,这可能会导致混淆的结果。

  • 'fetch' -通过在UPDATE或DELETE之前执行SELECT或使用RETURNURN(如果数据库支持)检索受影响行的主键标识,以便可以使用新值(更新)刷新受操作影响的内存中对象或从 Session (删除)。请注意,在给定的情况下,此同步策略不可用 update()delete() 构造为以下项指定列 UpdateBase.returning() 明确地说。

  • 'evaluate' -评估Python中UPDATE或DELETE语句中给定的WHERE条件,以在 Session 。此方法不会增加任何往返行程,并且在没有返回支持的情况下效率更高。对于具有复杂条件的UPDATE或DELETE语句, 'evaluate' 策略可能无法计算Python中的表达式,并将引发错误。如果发生这种情况,请使用 'fetch' 取而代之的是行动的战略。

    警告

    这个 "evaluate" 如果更新操作要在 Session 它有许多对象已经过期,因为它必须在这些对象所在的位置刷新这些对象,这些对象将为每个对象发出一个SELECT。这个 Session 如果在多个 Session.commit() 电话和 Session.expire_on_commit 标志的默认值为 True .

警告

Additional Caveats for ORM-enabled updates and deletes

支持ORM的UPDATE和DELETE特性绕过了ORM的工作单元自动化,而能够发出一个同时匹配多行的UPDATE或DELETE语句,而不会带来复杂性。

  • 这些操作在Python中不提供关系的级联-假定为任何需要的外键引用配置了ON UPDATE CASCADE和/或ON DELETE CASCADE,否则,如果实施外键引用,数据库可能会发出完整性冲突。

  • 更新或删除后,中的依赖对象 Session 受相关表上的ON UPDATE CASCADE或ON DELETE CASCADE影响的可能不包含当前状态;一旦 Session 过期,通常发生在 Session.commit() 或者可以通过使用 Session.expire_all() .

  • 这个 'fetch' 该策略在不支持返回的数据库(如MySQL或SQLite)上运行时,会导致发出额外的SELECT语句,这可能会降低性能。在开发时使用SQL回显来评估发出的SQL的影响。

  • 启用ORM的更新和删除不会自动处理联接表继承。如果操作针对多个表,通常应针对单个表使用单独的UPDATE/DELETE语句。有些数据库支持多个表更新。类似指南,详见 多个表更新 可以应用。

  • 为了将多态标识限制为单表继承映射的特定子类,需要使用WHERE条件 自动包含 . 这只适用于没有自己的表的子类mapper。

    在 1.4 版更改: ORM更新/删除现在自动适应为单继承映射添加的WHERE条件。

  • 这个 with_loader_criteria() 选项 支持 通过ormupdate和delete操作;这里的条件将添加到所发出的update或delete语句中,并在“synchronize”过程中被考虑在内。

  • 为了使用事件处理程序拦截启用了ORM的更新和删除操作,请使用 SessionEvents.do_orm_execute() 事件。

选择与UPDATE内联的ORM对象..正在返回或插入..正在返回

此部分已移动。看见 使用INSERT、UPDATE和ON CONFLICATION(即upsert)返回ORM对象

自动开始

1.4 新版功能: 本节介绍SQLAlChemy 1.4中的新行为,该行为不适用于以前的版本。有关“自动开始”更改的更多详细信息,请访问 会话具有新的“自动注册”行为

这个 Session 对象具有一种称为 自动开始 。这表明 Session 执行任何工作后,将在内部认为自己处于“事务性”状态。 Session 的内部状态的修改,或者涉及对 Session 关于对象状态更改或需要数据库连接的操作。

Session 是第一次构造的,则不存在事务状态。事务状态是自动开始的,当方法(如 Session.add()Session.execute() 将被调用,或者类似地,如果 Query 被执行以返回结果(最终使用 Session.execute() ),或者如果在 persistent 对象。

事务状态可以通过访问 Session.in_transaction() 方法,该方法返回 TrueFalse 指示“自动开始”步骤是否已经进行。虽然通常不需要,但 Session.get_transaction() 方法将返回实际的 SessionTransaction 对象,该对象表示此事务状态。

对象的事务状态。 Session 也可以显式启动,方法是调用 Session.begin() 方法。调用此方法时, Session 无条件地置于“事务性”状态。 Session.begin() 可以用作上下文管理器,如 构建begin/commit/rollback块

在 1.4.12 版更改: -如果修改了对象属性,则立即正确发生自动开始;以前不会发生这种情况。

提交

Session.commit() 用于提交当前事务(如果有)。当没有适当的事务时,该方法以静默方式传递。

什么时候 Session.commit() 操作当前打开的事务,则它总是首先发出 Session.flush() 预先将任何剩余状态刷新到数据库;这与“自动刷新”设置无关。

在那之后, Session.commit() 然后提交实际的一个或多个数据库事务(如果有的话)。

最后,对象中的所有对象 Sessionexpired 随着交易的结束。这是因为,当下次访问实例时,无论是通过属性访问还是通过它们出现在SELECT结果中,它们都会收到最新的状态。此行为可能由 Session.expire_on_commit 标志,该标志可以设置为 False 当这种行为不受欢迎时。

在 1.4 版更改: 这个 Session 对象现在具有延迟的“开始”行为,如 autobegin 。如果没有开始事务,像这样的方法 Session.commit()Session.rollback() 没有任何效果。在1.4之前不会观察到此行为,因为在非自动提交模式下,事务将始终隐式存在。

参见

自动开始

回滚

Session.rollback() 回滚当前事务(如果有)。当没有适当的事务时,该方法以静默方式传递。

对于默认配置的会话,在事务通过以下任一方式开始后,会话的回滚后状态 autobegin 或通过调用 Session.begin() 方法显式,如下所示:

  • 所有事务都会回滚,所有连接都会返回到连接池,除非会话直接绑定到某个连接,在这种情况下,连接仍然保持不变(但仍会回滚)。

  • 最初在 pending 说明何时将它们添加到 Session 在事务的生命周期内将被删除,对应于它们的insert语句将被回滚。其属性的状态保持不变。

  • 标记为的对象 deleted 在事务的生命周期内,将提升回 persistent 状态,与要回滚的DELETE语句相对应。请注意,如果这些对象是第一个 pending 在事务中,该操作优先。

  • 所有未删除的对象都完全过期-这与 Session.expire_on_commit 设置。

了解了这种状态后, Session 可以在回滚发生后安全地继续使用。

在 1.4 版更改: 这个 Session 对象现在具有延迟的“开始”行为,如 autobegin 。如果没有开始事务,像这样的方法 Session.commit()Session.rollback() 没有任何效果。在1.4之前不会观察到此行为,因为在非自动提交模式下,事务将始终隐式存在。

当一个 Session.flush() 如果失败,通常是由于主键、外键或“不可为空的”约束冲突等原因,会自动发出回滚(目前不可能在部分失败后继续刷新)。然而, Session 此时进入称为“非活动”的状态,调用应用程序必须始终调用 Session.rollback() 方法,以便 Session 可以返回到可用状态(也可以简单地关闭并丢弃)。请参阅以下位置的常见问题解答条目 “由于在刷新过程中出现以前的异常,此会话的事务已回滚。”(或类似) 以供进一步讨论。

参见

自动开始

关闭

这个 Session.close() 方法问题A Session.expunge_all() 从会话中移除所有ORM映射对象,以及 releases 来自的任何事务/连接资源 Engine 绑定到的对象。当连接返回到连接池时,事务状态也将回滚。

Session 是封闭的,它基本上处于最初建造时的原始状态,并且 可以再次使用 . 从这个意义上说 Session.close() 方法更像是“重置”回干净状态,而不像“数据库关闭”方法。

建议 Session 受限于 Session.close() 尤其是在最后 Session.commit()Session.rollback() 未使用方法。这个 Session 可以用作上下文管理器来确保 Session.close() 被称为:

with Session(engine) as session:
    result = session.execute(select(User))

# closes session automatically

在 1.4 版更改: 这个 Session 对象功能延迟的“开始”行为,如中所述 autobegin 。事件后不再立即开始新事务。 Session.close() 方法被调用。

会话常见问题

到目前为止,许多用户已经对会话产生了疑问。本节介绍了一个小型常见问题解答(请注意,我们还有一个 real FAQ )在使用 Session .

我什么时候做 sessionmaker 是吗?

只是一次,在应用程序的全局范围内的某个地方。它应该被视为应用程序配置的一部分。如果应用程序在一个包中有三个.py文件,则可以将 sessionmaker 线在你 __init__.py 文件;从这一点开始,您的其他模块会说“从mypackage导入会话”。那样的话,其他人都会用 Session() ,该会话的配置由该中心点控制。

如果应用程序启动、导入但不知道它将连接到哪个数据库,则可以绑定 Session 在“Class”(等级)级别,稍后使用 sessionmaker.configure() .

在本节的示例中,我们将经常显示 sessionmaker 在我们实际调用的行的正上方创建 Session . 但这只是举个例子!事实上, sessionmaker 在模块级的某个地方。要实例化的调用 Session 然后将放置在应用程序中数据库对话开始的位置。

我什么时候做一个 Session ,什么时候提交,什么时候关闭?

tl;dr;

  1. 作为一般规则,保持会话的生命周期 独立和外部 访问和/或操作数据库数据的函数和对象。这将大大有助于实现可预测和一致的事务范围。

  2. 确保您清楚地了解事务的开始和结束位置,并保留事务 短的 也就是说,它们以一系列操作结束,而不是无限期地保持打开状态。

A Session 通常在逻辑操作开始时构造,在逻辑操作开始时可能需要数据库访问。

这个 Session ,每当它用于与数据库通信时,只要它开始通信就开始数据库事务。此事务处理将继续进行,直到 Session 回滚、提交或关闭。这个 Session 如果再次使用新事务,将在上一个事务结束后开始新事务;在此之后,将执行 Session 能够跨多个事务拥有寿命,但一次只能有一个。我们将这两个概念称为 交易范围会话范围 .

通常很难确定开始和结束 Session 尽管各种各样的应用程序架构可能会带来挑战性的情况。

一些示例场景包括:

  • 网络应用程序。在这种情况下,最好使用正在使用的web框架提供的SQLAlchemy集成。否则,基本模式是创建 Session 在web请求开始时,调用 Session.commit() 方法,这些请求执行POST、PUT或DELETE操作,然后在web请求结束时关闭会话。这通常也是一个好主意 Session.expire_on_commit 设置为False,以便后续访问来自 Session 如果事务已经提交,则视图层中不需要发出新的SQL查询来刷新对象。

  • 从子分叉派生的后台守护程序将要创建一个 Session 在每个子进程的本地,使用它 Session 在叉处理的“作业”的整个生命周期中,当作业完成时将其拆下。

  • 对于命令行脚本,应用程序将创建单个全局 Session 当程序开始执行其工作时建立的,并在程序完成其任务时将其正确提交。

  • 对于GUI界面驱动的应用程序, Session 最好是在用户生成的事件范围内,如按钮按下。或者,作用域可能对应于显式的用户交互,例如用户“打开”一系列记录,然后“保存”它们。

作为一般规则,应用程序应该管理会话的生命周期 外部的 处理特定数据的函数。这是关注点的基本分离,使特定于数据的操作与它们访问和操作该数据的上下文无关。

例如。 不要这样做 ::

### this is the **wrong way to do it** ###

class ThingOne(object):
    def go(self):
        session = Session()
        try:
            session.query(FooBar).update({"x": 5})
            session.commit()
        except:
            session.rollback()
            raise

class ThingTwo(object):
    def go(self):
        session = Session()
        try:
            session.query(Widget).update({"q": 18})
            session.commit()
        except:
            session.rollback()
            raise

def run_my_program():
    ThingOne().go()
    ThingTwo().go()

保留会话的生命周期(通常是事务) 独立和外部 . 下面的示例演示了它的外观,另外还使用了Python上下文管理器(即 with: 关键字)以管理 Session 以及它的自动交易:

### this is a **better** (but not the only) way to do it ###

class ThingOne(object):
    def go(self, session):
        session.query(FooBar).update({"x": 5})

class ThingTwo(object):
    def go(self, session):
        session.query(Widget).update({"q": 18})

def run_my_program():
    with Session() as session:
        with session.begin():
            ThingOne().go(session)
            ThingTwo().go(session)

在 1.4 版更改: 这个 Session 可以用作上下文管理器,而不使用外部助手函数。

会话是缓存吗?

是的…不。它有点用作缓存,因为它实现了 identity map 模式,并存储键控其主键的对象。但是,它不执行任何类型的查询缓存。这意味着,如果你说 session.query(Foo).filter_by(name='bar') ,即使 Foo(name='bar') 就在那里,在身份图中,会话不知道这一点。它必须向数据库发出SQL,返回行,然后当它看到行中的主键时, then 它可以在本地标识映射中查看对象已经存在。只有当你说 query.get({{some primary key}})Session 不必发出查询。

此外,默认情况下,会话使用弱引用存储对象实例。这也破坏了将会话用作缓存的目的。

这个 Session 不是设计为全局对象,每个人都从中作为对象的“注册表”进行查询。这更像是 二级缓存 . SQLAlchemy提供了一种模式,用于实现二级缓存,使用 dogpile.cache ,通过 狗堆缓存 例子。

我怎么才能拿到 Session 为了某个目标?

使用 Session.object_session() ClassMethod可用于 Session ::

session = Session.object_session(someobject)

更新者 运行时检查API 系统也可以使用:

from sqlalchemy import inspect
session = inspect(someobject).session

会话线程安全吗?

这个 Session 非常适合用于 non-concurrent 时尚,通常一次只穿一条线。

这个 Session 应该以这样的方式使用:在单个事务中,对于单个操作系列,存在一个实例。获得这种效果的一种权宜之计是将 Session 使用当前线程(请参见 上下文/线程本地会话 作为背景)。另一种方法是使用 Session 在函数之间传递,否则不与其他线程共享。

更重要的是你不应该 want 使用多个并发线程的会话。这就像让每个人在一家餐厅都吃同一个盘子。会话是用于特定任务集的本地“工作区”;您不希望或不需要与正在执行其他任务的其他线程共享该会话。

确保 Session 一次只在一个并发线程中使用被称为“无共享”并发方法。但实际上,不是分享 Session 意味着一个更重要的模式;它不仅仅意味着 Session 对象本身,但也 与该会话关联的所有对象 必须保持在单个并发线程的范围内。与 Session 基本上是通过数据库连接访问的数据库行中的数据代理,因此就像 Session 整个对象集本身实际上只是数据库连接(或连接)的大型代理。最终,我们远离并发访问的主要是DBAPI连接本身;但是由于 Session 与之相关联的所有对象都是DBAPI连接的代理,整个图对于并发访问基本上是不安全的。

如果事实上有多个线程参与同一个任务,那么您可以考虑在这些线程之间共享会话及其对象;但是,在这种极不寻常的情况下,应用程序需要确保实现适当的锁定方案,以便 同时发生的 访问 Session 或者它的状态。在这种情况下,更常见的方法是保持 Session 每个并发线程,但改为 copy 对象来自一个 Session 对另一个,通常使用 Session.merge() 方法将一个对象的状态复制到另一个对象的本地新对象中 Session .

Previous: 使用会话 Next: 国家管理