会话基础¶
会话有什么作用?¶
在最一般的意义上,Session
建立所有对话
替换为数据库,并表示所有对象的“保持区”,这些对象
您在其生命周期内已加载或与之关联。它提供
进行 SELECT 和其他查询的接口,这些查询将返回并修改
ORM 映射的对象。 ORM 对象本身维护在
Session
,在称为身份映射的结构中 - 一种维护每个对象的唯一副本的数据结构,其中“唯一”表示“只有一个具有特定主键的对象”。
Session 最常见的使用模式以大部分无状态的形式开始。发出查询或保留其他对象后,它会从与
Session
关联的 Engine
请求连接资源,然后在该连接上建立事务。此交易在 Session
之前一直有效
被指示提交或回滚事务。 当事务
ends,则与引擎
关联的连接资源
释放到引擎管理的连接池中。然后,新事务从新的 connection checkout 开始。
由 Session
维护的 ORM 对象被检测
这样,每当在 Python 中修改属性或集合时,都会显示
程序中,会生成一个 change 事件,该事件由
会话
。每当要查询数据库或即将提交事务时,首先执行 Session
将内存中存储的所有待处理更改刷新到数据库。这称为 Unit of work 模式。
当使用 Session
时,将它作为代理对象维护的 ORM 映射对象视为数据库行是有用的,这些对象是 Session
持有的事务的本地对象。 为了维护
state 匹配数据库中的实际内容,则有一个
各种事件,这些事件将导致对象重新访问数据库,以便
保持同步。 可以将对象从
Session
并继续使用它们,尽管这种做法有其注意事项。通常,当您想再次使用分离的对象时,您会将分离的对象与另一个 Session
重新关联,以便它们可以恢复表示数据库状态的正常任务。
使用 Session 的基础知识¶
这里介绍了最基本的 Session
使用模式。
打开和关闭会话¶
Session
可以自行构建,也可以使用
sessionmaker
类。 它通常传递一个
Engine
作为前部连接源。典型用途可能如下所示:
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
# create session and add objects
with Session(engine) as session:
session.add(some_object)
session.add(some_other_object)
session.commit()
在上面,Session
使用 Engine
实例化
与特定数据库 URL 相关联。 然后在 Python 中使用它
上下文管理器(即 with:
语句),以便在块的末尾自动关闭;这相当于调用 Session.close()
方法。
对 Session.commit()
的调用是可选的,只有当我们对 Session
所做的工作包含要持久化到数据库的新数据时,才需要调用它。如果我们只发出 SELECT 调用并且不需要编写任何更改,那么对 Session.commit()
的调用就没有必要了。
注意
请注意,在调用 Session.commit()
后,显式或
使用上下文管理器时,与
会话
已过期,这意味着它们的内容被擦除到
在下一个事务中重新加载。如果这些对象是
detached 时,它们将不起作用,直到与新 Session
重新关联,除非 Session.expire_on_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()
上面所示的长格式作序列可以是
通过使用
Session.begin()
返回的 SessionTransaction
对象
方法,它为相同的
操作:
# create session and add objects
with Session(engine) as session:
with session.begin():
session.add(some_object)
session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()
更简洁地说,这两个上下文可以组合在一起:
# create session and add objects
with Session(engine) as session, session.begin():
session.add(some_object)
session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()
使用 sessionmaker¶
sessionmaker
的目的是为
Session
对象。由于应用程序通常在模块范围内具有 Engine
对象,因此 sessionmaker
可以为
针对此引擎构造的 Session
对象:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# an Engine, which the Session will use for connection
# resources, typically in module scope
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)
# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
session.add(some_object)
session.add(some_other_object)
session.commit()
# closes the session
sessionmaker
类似于 Engine
作为函数级会话/连接的模块级工厂。 因此
它也有自己的 SessionMaker.begin()
方法,类似于 Engine.begin(),
它返回一个 Session
对象并维护一个 begin/commit/rollback 块:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# an Engine, which the Session will use for connection
# resources
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)
# we can now construct a Session() and include begin()/commit()/rollback()
# at once
with Session.begin() as session:
session.add(some_object)
session.add(some_other_object)
# commits the transaction, closes the session
在上述情况下,Session
将同时提交其事务,并且 Session
将被关闭,此时上述
with:
块结束。
当您编写应用程序时,
sessionmaker
工厂的作用域应与
Engine
对象,通常由 create_engine()
创建,通常位于模块级别或全局范围内。由于这些对象都是工厂,因此它们可以同时被任意数量的函数和线程使用。
查询¶
查询的主要方法是使用 select()
construct 创建一个 Select
对象,然后使用 Session.execute()
和
Session.scalars()
的 然后,结果将以
Result
对象,包括
ScalarResult 的 ScalarResult
中。
SQLAlchemy ORM 查询的完整指南可以在
ORM 查询指南。以下是一些简短的示例:
from sqlalchemy import select
from sqlalchemy.orm import Session
with Session(engine) as session:
# query for ``User`` objects
statement = select(User).filter_by(name="ed")
# list of ``User`` objects
user_obj = session.scalars(statement).all()
# query for individual columns
statement = select(User.name, User.fullname)
# list of Row objects
rows = session.execute(statement).all()
在 2.0 版更改: “2.0” 样式的查询现在是标准的。 看
2.0 迁移 - 1.x 系列迁移说明的 ORM 用法。
另请参阅
添加新项目或现有项目¶
Session.add()
用于将实例放置在 session 中。对于临时 (即全新的) 实例,这将产生在下一次 flush 时对这些实例执行 INSERT 的效果。对于持久性实例(即由此 session 加载),它们已经存在,不需要添加。已分离的实例
(即已从会话中删除)可能会重新与会话关联
使用此方法:
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()
方法将一个实例放入 Session 的对象列表中,以标记为已删除:
# mark two objects to be deleted
session.delete(obj1)
session.delete(obj2)
# commit (or flush)
session.commit()
Session.delete()
将对象标记为删除,这将导致为每个受影响的主键发出 DELETE 语句。在刷新待处理的删除之前,标记为 “delete” 的对象存在于 Session.deleted
集合中。在 DELETE 之后,它们将从 Session
中删除,这在事务提交后成为永久性的。
有各种与
Session.delete()
作,尤其是在如何处理与其他对象和集合的关系方面。在 Cascades 部分有更多关于它是如何工作的信息,但一般来说,规则是:
与通过relationship()
指令与已删除对象相关的映射对象对应的行不是 默认已删除。如果这些对象具有返回到要删除的行的外键约束,则这些列将设置为 NULL。如果列不可为 null,这将导致约束冲突。
要将 “SET NULL” 更改为相关对象行的 DELETE,请使用 delete relationship()
上的 cascade。
作为“多对多”表链接的表中的行,通过relationship.secondary
参数,则在所有情况下都会在删除它们引用的对象时删除。
当相关对象包含返回要删除的对象的外键约束,并且它们所属的相关集合当前未加载到内存中时,工作单元将发出 SELECT 来获取所有相关行,以便它们的主键值可用于在这些相关行上发出 UPDATE 或 DELETE 语句。这样,无需进一步指示的 ORM 将执行 ON DELETE CASCADE 的功能,即使这是在 CoreForeignKeyConstraint
上配置的 对象。relationship.passive_deletes
参数可用于调整此行为,并更自然地依赖于 “ON DELETE CASCADE”;当设置为 True 时,将不再执行此 SELECT作,但是本地存在的行仍将受到显式 SET NULL 或 DELETE 的约束。将relationship.passive_deletes
设置为字符串“all”
将禁用所有相关对象更新/删除。
当标记为删除的对象发生 DELETE 时,该对象不会自动从引用它的集合或对象引用中删除。当Session
过期时,这些集合可能会再次加载,以便该对象不再存在。但是,最好不要对这些对象使用Session.delete(),
而应该从其集合中删除该对象,然后使用 delete-orphan,以便将其作为该集合删除的次要效果进行删除。有关此示例,请参阅删除注释 - 删除从集合和标量关系引用的对象部分。
另请参阅
Delete - 描述“Delete Cascade”,它在删除潜在客户对象时将相关对象标记为要删除。
delete-orphan - 描述“删除孤立级联”,当相关对象与其引导对象取消关联时,它会将相关对象标记为要删除。
有关删除的说明 - 删除从集合和标量关系引用的对象 - 重要背景
Session.delete()
涉及在内存中刷新的关系。
刷新¶
当 Session
与其默认配置一起使用时,flush 步骤几乎总是透明地完成的。具体来说,刷新发生在由于 Query
或 2.0 样式的 Session.execute()
调用而发出任何单个 SQL 语句之前,以及在
Session.commit()
调用。
承诺。在以下情况下,它也发生在发出 SAVEPOINT 之前
使用 Session.begin_nested()。
可以随时通过调用
Session.flush()
方法:
session.flush()
在某些方法范围内自动发生的 flush 称为 autoflush。Autoflush 被定义为可配置的自动 flush 调用,它发生在方法的开头,包括:
Session.execute()
和其他 SQL 执行方法,当用于支持 ORM 的 SQL 结构时,例如引用 ORM 实体和/或 ORM 映射属性的select()
对象
调用Query
以将 SQL 发送到数据库时
在Session.merge()
方法中,在查询数据库之前
刷新对象时
当 ORM 延迟加载作针对已卸载的对象属性发生时。
也有一些点会无条件地发生 flush;这些点位于关键的交易边界内,其中包括:
在Session.commit()
方法的进程中
当使用Session.prepare()
2PC 方法时。
应用于前面的项目列表的 autoflush 行为可以通过构造 Session
或
sessionmaker
将 Session.autoflush
参数作为
错误
:
Session = sessionmaker(autoflush=False)
此外,可以在使用 Session
的流程中使用
Session.no_autoflush
上下文管理器:
with mysession.no_autoflush:
mysession.add(some_object)
mysession.flush()
重申一下:当 Session 有剩余的待处理更改时,无论
Session.commit()
和 Session.begin_nested()
等事务方法被调用时,无论任何 “autoflush” 设置如何,刷新进程总是发生的。
由于 Session
仅在 DBAPI 事务的上下文中向数据库调用 SQL,因此所有 “flush”作本身都只发生在
数据库事务(受
数据库的隔离级别
transaction),前提是 DBAPI 不在
驱动程序级别自动提交模式。这意味着,假设数据库连接在其事务设置中提供原子性,如果 flush 中的任何单个 DML 语句失败,则整个作将回滚。
当 flush 中发生故障时,为了继续使用同一个 Session
,对 Session.rollback()
的显式调用是
required,即使底层事务将具有
已回滚(即使数据库驱动程序在技术上处于
驱动程序级自动提交模式)。 这样,整个
所谓的 “subtransactions” 被始终维护。FAQ 部分
“由于刷新期间的上一个异常,此 Session 的事务已回滚。”(或类似名称)包含此行为的更详细描述。
另请参阅
“由于刷新期间的上一个异常,此 Session 的事务已回滚。”(或类似名称)- 有关原因的更多背景
当 flush 失败时,必须调用 Session.rollback()
。
按主键获取¶
由于 Session
使用身份映射,该 Map 通过主键引用当前的内存中对象,因此 Session.get()
method 作为通过主键定位对象的一种方式,首先
在当前身份映射中查找,然后查询数据库
对于非现值。 例如,要查找具有主键身份 (5, ) 的
User
实体:
my_user = session.get(User, 5)
Session.get()
还包括复合主键值的调用形式,这些值可以作为 Tuples 或 Dictionaries 传递,以及允许特定 loader 和执行选项的附加参数。有关完整的参数列表,请参见 Session.get()
。
另请参阅
过期 / 刷新¶
在使用
Session
是处理从数据库加载的对象上存在的状态的方法,以保持它们与事务的当前状态同步。SQLAlchemy ORM 基于身份映射的概念,因此,当从 SQL 查询“加载”对象时,将维护与特定数据库身份对应的唯一 Python 对象实例。这意味着,如果我们发出两个单独的查询,每个查询针对同一行,并返回一个映射的对象,则这两个查询将返回相同的 Python 对象:
>>> u1 = session.scalars(select(User).where(User.id == 5)).one()
>>> u2 = session.scalars(select(User).where(User.id == 5)).one()
>>> u1 is u2
True
接下来,当 ORM 从查询中返回行时,它将
跳过已加载的对象的属性填充。这里的设计假设是假设一个事务是完全隔离的,然后根据该事务未隔离的程度,应用程序可以根据需要采取步骤来刷新数据库事务中的对象。我正在使用我的会话重新加载数据,但它没有看到我在其他地方提交的更改
更详细地讨论了这个概念。
当 ORM 映射对象被加载到内存中时,有三种通用方法可以使用当前事务中的新数据刷新其内容:
expire() 方法 -Session.expire()
方法将擦除对象的选定或所有属性的内容,以便在下次访问它们时从数据库中加载它们,例如使用延迟加载模式:session.expire(u1) u1.some_attribute # <-- lazy loads from the transaction
refresh() 方法 - 密切相关的是Session.refresh()
方法,它执行Session.expire()
方法所做的所有作,但也会立即发出一个或多个 SQL 查询以实际刷新对象的内容:session.refresh(u1) # <-- emits a SQL query u1.some_attribute # <-- is refreshed from the transaction
populate_existing() 方法或执行选项 - 这是现在在 Populate Existing 中记录的执行选项;在旧格式中,它在Query
对象上作为Query.populate_existing()
方法。无论哪种形式,此作都表示应从数据库中的内容无条件地重新填充从查询返回的对象:u2 = session.scalars( select(User).where(User.id == 5).execution_options(populate_existing=True) ).one()
有关 refresh / expire 概念的进一步讨论,请访问
刷新 / 过期。
带有任意 WHERE 子句的 UPDATE 和 DELETE¶
SQLAlchemy 2.0 包括用于发出多种支持 ORM 的 INSERT、UPDATE 和 DELETE 语句的增强功能。有关文档,请参阅 ORM-Enabled INSERT、UPDATE 和 DELETE 语句中的文档。
自动开始¶
Session
对象具有一种称为 autobegin 的行为。这表明 Session
将在内部考虑自身
在执行任何工作后立即处于 “transactional” 状态
Session
,要么涉及对 Session
内部状态的修改,要么涉及需要数据库连接的作。
首次构造 Session
时,不存在事务状态。当 Session.add()
或 Session.execute()
等方法时,事务状态会自动开始
,或者如果执行 Query
以返回结果(最终使用 Session.execute()
),或者如果在持久化对象上修改了属性,则类似地。
可以通过访问
Session.in_transaction()
方法,该方法返回 True
或 False
指示 “autobegin” 步骤是否已进行。虽然通常不需要,
Session.get_transaction()
方法将返回实际的
SessionTransaction
对象。
Session 的事务状态也可以通过调用
Session.begin()
方法显式启动。当调用此方法时,Session
将无条件地置于 “transactional” 状态。Session.begin()
可以用作上下文管理器,如 Framing out a begin / commit / rollback 块中所述。
禁用 Autostart 以防止隐式事务¶
可以使用
Session.autobegin
参数设置为 False
。通过使用此参数,Session
将要求
Session.begin()
方法。在构造时以及任何 Session.rollback()
之后,
Session.commit()
或 Session.close()
方法,Session
不会隐式地开始任何新的事务,如果尝试使用 Session
而没有先调用 Session.begin(),
则会引发错误:
with Session(engine, autobegin=False) as session:
session.begin() # <-- required, else InvalidRequestError raised on next call
session.add(User(name="u1"))
session.commit()
session.begin() # <-- required, else InvalidRequestError raised on next call
u1 = session.scalar(select(User).filter_by(name="u1"))
2.0 版本中的新功能: 添加了 Session.autobegin
,允许禁用 “autobegin” 行为
提交¶
Session.commit()
用于提交当前事务。从本质上讲,这表明它在所有当前正在进行事务的数据库连接上发出 COMMIT
;从 DBAPI 的角度来看,这意味着 connection.commit()
在每个 DBAPI 连接上调用 DBAPI 方法。
当 Session
没有事务时,表明自上次调用 Session.commit()
以来没有在此 Session
上调用任何作,该方法将开始并提交一个仅限内部的“逻辑”事务,除非检测到待处理的刷新更改,否则该事务通常不会影响数据库,但仍将调用事件处理程序和对象过期规则。
Session.commit()
作无条件地发出
Session.flush()
在相关数据库上发出 COMMIT 之前
连接。如果未检测到待处理更改,则不会向
数据库。此行为不可配置,也不受
Session.autoflush
参数。
之后,假设 Session
绑定到
Engine
时,Session.commit()
将 COMMIT 实际的数据库事务(如果已启动)。提交后,与该事务关联的 Connection
对象将关闭,从而导致其底层 DBAPI 连接被释放回与引擎
关联的连接池,该引擎的
会话
已绑定。
对于绑定到多个引擎的 Session
(例如,如 分区策略中所述),将对每个引擎
/ 执行相同的 COMMIT 步骤。
Connection
中正在提交的 “logical” 事务中发挥作用。除非启用了两阶段功能,否则这些数据库事务彼此不协调。
其他连接交互模式也可用,只需将
Session
直接连接到 Connection
;在本例中,
它假定存在外部管理的事务,并且一个真实的
在这种情况下,不会自动发出 COMMIT;请参阅该部分
将 Session 加入 External Transaction (例如测试套件) 以获取有关此模式的背景信息。
最后,当事务被关闭时,Session
中的所有对象都会过期。这样,当下次通过属性访问或通过它们出现在 SELECT 结果中访问实例时,它们会收到最新的状态。此行为可能由 Session.expire_on_commit
标志控制,当此行为不可取时,该标志可能设置为 False
。
另请参阅
回滚¶
Session.rollback()
回滚当前事务(如果有)。当没有事务时,该方法以静默方式传递。
使用默认配置的会话,会话的回滚后状态,在事务通过 autobegin 开始之后
或通过调用 Session.begin()
method 显式地表示,如下所示:
数据库事务回滚。对于Session
绑定到单个Engine
,这意味着最多为当前正在使用的单个Connection
发出 ROLLBACK。对于绑定到多个Engine
的Session
对象 对象时,会为所有已签出的Connection
对象发出 ROLLBACK。
释放数据库连接。这遵循 提交 中记下的与连接相关的相同行为,其中 从Engine
获取的 Connection
对象 对象被关闭,导致 DBAPI 连接被释放到Engine
中的连接池。如果新事务开始,则从Engine
中签出新连接。
对于Session
直接绑定到Connection
,如将 Session 加入外部事务(例如测试套件)中所述,此Connection
将遵循Session.join_transaction_mode
参数,这可能涉及回滚 savepoint 或发出真正的 ROLLBACK。
在事务的生命周期内,当它们被添加到Session
时,最初处于待处理状态的对象将被删除,对应于它们的 INSERT 语句被回滚。其属性的状态保持不变。
在事务的生命周期内标记为已删除的对象将提升回持久状态,对应于其 DELETE 语句的回滚。请注意,如果这些对象在事务中首先处于待处理状态,则该作优先。
所有未删除的对象都已完全过期 - 这与Session.expire_on_commit
设置。
理解该状态后,Session
可以在回滚发生后安全地继续使用。
在 1.4 版本发生变更: Session
对象现在具有延迟的 “begin” 行为,如 autobegin 中所述。如果没有事务开始,则 Session.commit()
和
Session.rollback()
无效。在 1.4 之前不会观察到此行为,因为在非 auto-commit 模式下,事务将始终隐式存在。
当 Session.flush()
失败时,通常是由于主键、外键或“不可为空”约束冲突等原因,会自动发出 ROLLBACK(目前在部分失败后无法继续刷新)。但是,Session
会进入一种称为
“inactive”,并且调用应用程序必须始终调用
Session.rollback()
方法,以便
Session
可以返回到可用状态(也可以简单地关闭并丢弃)。请参阅“此会话的事务已因刷新期间的先前异常而被回滚”中的常见问题解答条目。(或类似名称)以供进一步讨论。
另请参阅
关闭¶
Session.close()
方法发出一个 Session.expunge_all()
从会话中删除所有 ORM 映射的对象,并从它绑定到的 Engine
对象中释放任何事务/连接资源。当连接返回到连接池时,事务状态也会回滚。
默认情况下,当 Session
关闭时,它基本上处于第一次构造时的原始状态,并且可以再次使用。从这个意义上说,Session.close()
方法更像是 “reset” 回到 clean 状态,而不是 “database close” 方法。在这种作模式下,方法 Session.reset()
是
Session.close() 的 Session.close()
方法,并且其行为方式相同。
可以通过将参数 Session.close_resets_only
设置为 False
来更改 Session.close()
的默认行为,表示在该方法之后不能重用 Session
Session.close()
已被调用。在这种作模式下,
Session.reset()
方法将允许多次 “重置” 会话,当
Session.close_resets_only
设置为 True
。
2.0.22 新版功能.
建议通过在结束时调用 Session.close()
来限制 Session
的范围,尤其是在
不使用 Session.commit()
或 Session.rollback()
方法。Session
可以用作上下文管理器,以确保 Session.close()
被调用:
with Session(engine) as session:
result = session.execute(select(User))
# closes session automatically
在 1.4 版本发生变更: Session
对象具有延迟的 “begin” 行为,如 autobegin 中所述。在调用 Session.close()
方法后,不再立即开始新事务。
Session 常见问题¶
此时,许多用户已经对会话有疑问。本节介绍了一个迷你 FAQ(请注意,我们还有一个真正的 FAQ),列出了使用 Session
时遇到的最基本问题。
我何时创建 sessionmaker
?¶
只需一次,在应用程序的全局范围内的某个位置。应将其视为应用程序配置的一部分。如果您的应用程序在一个包中有三个 .py 文件,例如,您可以将 sessionmaker
行放在 __init__.py
文件中;从那时起,您的其他模块说 “from mypackage import Session”。这样,其他人只需使用 Session(),
并且该 session 的配置由该中心点控制。
如果您的应用程序启动,则执行导入,但不知道导入内容
数据库,则可以将
稍后使用
sessionmaker.configure()
将 session 添加到引擎。
在本节的示例中,我们将经常展示
sessionmaker
在我们实际调用 Session
的行的正上方创建。但这只是为了例子!实际上,会话创建者
将位于模块级别的某个位置。对实例化 Session
的调用
将放置在应用程序中的 database
对话开始了。
何时构造 Session
,何时提交,何时关闭它?¶
Session
通常在逻辑作开始时构造,其中可能会预见到数据库访问。
每当 Session
用于与数据库通信时,它都会在开始通信时立即开始数据库事务。此事务将一直进行中,直到会话
已回滚、提交或关闭。 如果再次使用 Session
会话,则在上一个事务结束之后开始一个新的事务;由此可见,Session
能够在许多事务中具有生命周期,但只有
一次一个。 我们将这两个概念称为交易范围
和 session 范围。
通常,确定开始和结束 Session
范围的最佳点并不难,尽管可能的各种应用程序体系结构可能会带来具有挑战性的情况。
一些示例场景包括:
Web 应用程序。在这种情况下,最好使用正在使用的 Web 框架提供的 SQLAlchemy 集成。否则,基本模式是在 Web 请求开始时创建一个Session
,在执行 POST、PUT 或 DELETE,然后关闭会话的 Web 请求 在 Web 请求的末尾。 设置
Session.expire_on_commit
False,以便对来自视图层中Session
的对象的后续访问不需要发出新的 SQL 查询来刷新对象(如果事务已经提交)。
从子 fork 生成的后台守护进程希望在每个子进程本地创建一个Session
,在 fork 正在处理的 “job” 的生命周期中与该Session
一起工作,然后在 job 完成时将其拆除。
对于命令行脚本,应用程序将创建一个全局Session 的 session
,该会话在程序开始执行其工作时建立,并在程序完成其任务时立即提交它。
对于 GUI 接口驱动的应用程序,Session
的作用域 可能最好位于用户生成的事件(如按钮 推。 或者,范围可能对应于显式用户交互,例如 用户 “打开” 一系列记录,然后 “保存” 它们。
作为一般规则,应用程序应该在处理特定数据的函数的外部管理会话的生命周期。这是一种基本的关注点分离,它使特定于数据的作与它们访问和作该数据的上下文无关。
例如,不要这样做:
### this is the **wrong way to do it** ###
class ThingOne:
def go(self):
session = Session()
try:
session.execute(update(FooBar).values(x=5))
session.commit()
except:
session.rollback()
raise
class ThingTwo:
def go(self):
session = Session()
try:
session.execute(update(Widget).values(q=18))
session.commit()
except:
session.rollback()
raise
def run_my_program():
ThingOne().go()
ThingTwo().go()
保留会话(通常是事务)的生命周期
分离和外部。下面的示例说明了它可能是什么样子,并且还使用了 Python 上下文管理器(即 with:
keyword) 以便自动管理 Session
及其事务的范围:
### this is a **better** (but not the only) way to do it ###
class ThingOne:
def go(self, session):
session.execute(update(FooBar).values(x=5))
class ThingTwo:
def go(self, session):
session.execute(update(Widget).values(q=18))
def run_my_program():
with Session() as session:
with session.begin():
ThingOne().go(session)
ThingTwo().go(session)
在 1.4 版本发生变更: Session
可以用作上下文管理器,而无需使用外部辅助函数。
Session 是缓存吗?¶
哎呀...不。它在某种程度上用作缓存,因为它实现了
身份映射模式,并存储与其主键键相关的对象。
但是,它不执行任何类型的查询缓存。这意味着,如果你说
session.scalars(select(Foo).filter_by(name='bar'))
,即使 Foo(name='bar')
就在那里,在身份映射中,会话对此一无所知。
它必须向数据库发出 SQL,取回行,然后在
看到该行中的主键,那么它可以在本地身份中查找
map 并查看对象是否已存在。只有当你说
query.get({some primary key})
中
Session
不必发出查询。
此外,默认情况下,Session 使用弱引用存储对象实例。这也违背了将 Session 用作缓存的目的。
Session
并不是被设计成一个全局对象,每个人都可以从中作为对象的 “注册表” 进行查询。这更像是二级缓存的工作。SQLAlchemy 通过 Dogpile Caching 示例提供了一种使用 dogpile.cache 实现二级缓存的模式。
如何获取某个对象的 Session
?¶
使用 Session
上可用的 Session.object_session()
类方法:
session = Session.object_session(someobject)
还可以使用较新的 Runtime Inspection API 系统:
from sqlalchemy import inspect
session = inspect(someobject).session