Python SQLAlchemy:创建与重用会话

Python SQLAlchemy:创建与重用会话,python,sqlalchemy,Python,Sqlalchemy,只是一个简单的问题:SQLAlchemy调用sessionmaker()一次,但每次需要与DB对话时都调用生成的Session()类。对我来说,这意味着第二次我会做我的第一次会话。添加(x)或类似的事情,我会先做 from project import Session session = Session() 到目前为止,我所做的是在我的模型中调用一次session=session(),然后始终在应用程序中的任何位置导入相同的会话。由于这是一个web应用程序,这通常意味着相同的(当执行一个视图时

只是一个简单的问题:SQLAlchemy调用
sessionmaker()
一次,但每次需要与DB对话时都调用生成的
Session()
类。对我来说,这意味着第二次我会做我的第一次
会话。添加(x)
或类似的事情,我会先做

from project import Session
session = Session()
到目前为止,我所做的是在我的模型中调用一次
session=session()
,然后始终在应用程序中的任何位置导入相同的会话。由于这是一个web应用程序,这通常意味着相同的(当执行一个视图时)

但区别在哪里呢?一直使用一个会话与在我的功能完成之前将其用于数据库内容,然后在下次我想与数据库对话时创建一个新会话相比,有什么缺点

我知道如果我使用多个线程,每个线程都应该有自己的会话。但是使用
作用域会话()
,我已经确定了这个问题不存在,是吗

请澄清我的任何假设是否错误。

sessionmaker()
是一个工厂,它鼓励在一个地方放置用于创建新的
Session
对象的配置选项。它是可选的,因为只要您需要一个新的
会话,您就可以轻松地调用
会话(bind=engine,expire\u on\u commit=False)
,除了它的冗长和冗余之外,我想阻止小规模“助手”的泛滥每个人都以某种新的、更令人困惑的方式来处理这种冗余问题

因此,
sessionmaker()
只是一个工具,可以在需要时帮助您创建
Session
对象

下一部分。我想问题是,在不同的时间点创建一个新的
Session()
,与一直使用一个会话有什么区别。答案不是很清楚<代码>会话
是一个容器,用于存放您放入其中的所有对象,然后它还跟踪打开的事务。在调用
rollback()
commit()
时,事务已结束,
会话
与数据库没有连接,直到再次调用它发出SQL。如果对象没有挂起的更改,则它与映射对象的链接是弱引用,因此即使在这方面,
会话
也会在应用程序丢失对映射对象的所有引用时将自身清空回全新状态。如果保留默认的
“提交时过期”
设置,则所有对象在提交后都将过期。如果该
会话
挂起五到二十分钟,并且下次使用时数据库中的所有内容都已更改,则下次访问这些对象时,它将加载所有全新的状态,即使它们已在内存中存储了二十分钟

在web应用程序中,我们通常会说,嘿,为什么不针对每个请求创建一个全新的
会话
,而不是反复使用同一个会话呢。这种做法确保新请求开始时“干净”。如果上一个请求中的某些对象尚未被垃圾收集,并且如果您已经关闭了
“expire\u on\u commit”
,那么上一个请求中的某些状态可能仍然存在,并且该状态可能非常旧。如果您小心地保持启用“提交时过期”,并在请求结束时明确调用“提交”()或“回滚”(),那么这很好,但是如果您从全新的
会话开始,那么您甚至没有任何问题可以从头开始。因此,用一个新的<代码>会话<代码>启动每个请求的想法实际上是确保您开始新的最简单的方法,并且使用<代码> ExpReRyOnOnOpjs几乎是可选的,因为这个标志可以在一系列操作的中间调用一个额外的SQL,用于调用<代码>提交()/<代码>的操作。不确定这是否回答了你的问题


下一轮是关于线程的讨论。如果您的应用程序是多线程的,我们建议您确保正在使用的
会话是本地的<默认情况下,code>scoped_session()
使其成为当前线程的本地线程。在web应用程序中,本地请求实际上更好。Flask SQLAlchemy实际上向
scoped_session()
发送一个自定义的“scope函数”,以便获得一个请求范围的会话。平均金字塔应用程序将会话粘贴到“请求”注册表中。当使用类似的方案时,“根据请求创建新会话开始”的想法看起来仍然是最直接的方式来保持事情的正确性。

除了zzzeek的优秀答案之外,这里有一个简单的方法来快速创建一次性的自封闭会话:

from contextlib import contextmanager

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

@contextmanager
def db_session(db_url):
    """ Creates a context with an open SQLAlchemy session.
    """
    engine = create_engine(db_url, convert_unicode=True)
    connection = engine.connect()
    db_session = scoped_session(sessionmaker(autocommit=False, autoflush=True, bind=engine))
    yield db_session
    db_session.close()
    connection.close()
用法:

from mymodels import Foo

with db_session("sqlite://") as db:
    foos = db.query(Foo).all()

哇,这回答了我关于SQLAlchemy部分的所有问题,甚至还添加了一些关于烧瓶和金字塔的信息!附加奖励:开发者回答;)我希望我能不止投一次票。非常感谢你!如果可能的话,有一点需要澄清:您说在提交时过期“会导致大量额外的SQL”。。。你能提供更多的细节吗?我认为expire\u on\u commit只关心RAM中发生的事情,而不是数据库中发生的事情。如果再次使用同一会话,并且某些对象仍在该会话中挂起,expire\u on\u commit可能会导致更多SQL,当您访问它们时,您将为它们中的每一个选择一行,因为它们各自刷新新事务的状态。您好,@zzzeek。谢谢你的回答。我对python非常陌生,有几点我想澄清:1)当我通过调用session()方法创建新的“session”时,我理解正确吗?它将创建SQL事务,然后事务将被打开,直到我提交/回滚会话为止?2) session()是否每次都使用某种连接池或与sql建立新连接?对于我(使用Snowflak