SQLAlchemy:创建与重用会话

jav*_*vex 84 python sqlalchemy

只是一个简单的问题:SQLAlchemy 谈到调用sessionmaker()一次,但Session()每次需要与数据库通信时调用结果类.对我来说,这意味着第二个我会做我的第一个session.add(x)或类似的东西,我会先做

from project import Session
session = Session()
Run Code Online (Sandbox Code Playgroud)

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

但差异在哪里?在我的功能完成之前,一直使用一个会话反对我的数据库使用它然后在我想要与我的数据库交谈时创建一个新会话的缺点是什么?

如果我使用多个线程,我会得到它,每个线程应该得到自己的会话.但是使用scoped_session(),我已经确定问题不存在,是吗?

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

zzz*_*eek 192

sessionmaker()它是一个工厂,它鼓励Session在一个地方放置用于创建新对象的配置选项.它是可选的,因为你可以Session(bind=engine, expire_on_commit=False)随时轻松地调用你需要的新东西Session,除了它的冗长和冗余,我想阻止小规模"助手"的扩散,每个人都接近一些新的冗余问题而且更混乱的方式.

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

下一部分.我认为问题是,在不同的点上制作一个新Session()点与仅使用一个点之间有什么区别.答案,不是很多. Session是您放入其中的所有对象的容器,然后它还跟踪打开的事务.在您调用rollback()commit(),事务结束时,并且在Session调用它再次发出SQL之前,它与数据库没有任何关联.它保存到映射对象的链接是弱引用的,前提是对象没有待处理的更改,因此即使在这方面,Session当应用程序丢失对映射对象的所有引用时,它将自身清空回到全新状态.如果保留默认"expire_on_commit"设置,则提交后所有对象都将过期.如果它Session挂起五到二十分钟,并且下次你使用它时数据库中的各种事情都发生了变化,那么下次访问这些对象时它会加载所有全新的状态,即使它们已经存在于内存中二十分钟

在Web应用程序中,我们通常会说,嘿,为什么不在Session每个请求上创建一个全新的,而不是一遍又一遍地使用相同的.这种做法可确保新请求开始"干净".如果先前请求中的某些对象尚未被垃圾收集,并且如果您已经关闭"expire_on_commit",那么前一个请求中的某些状态可能仍然存在,并且该状态甚至可能已经过时了.如果你小心地expire_on_commit打开并且明确打电话commit()或者rollback()在请求结束时,那就没关系,但是如果你从一个全新的开始Session,那么你甚至没有任何问题要求你开始干净.因此,使用new启动每个请求的想法Session实际上只是确保您重新开始的最简单方法,并且使用expire_on_commit几乎是可选的,因为这个标志会为调用的操作带来大量额外的SQL commit()在一系列操作的中间.不确定这是否回答了你的问题.

下一轮就是你提到的线程问题.如果您的应用是多线程的,我们建议您确保Session使用的是本地的...某些东西. scoped_session()默认情况下,它使其成为当前线程的本地.在Web应用程序中,请求的本地实际上甚至更好.Flask-SQLAlchemy实际上发送了一个自定义的"范围函数",scoped_session()以便您获得请求范围的会话.平均Pyramid应用程序将会话粘贴到"请求"注册表中.当使用这样的方案时,"在请求开始时创建新会话"的想法继续看起来像是保持正确的最直接的方式.

  • 哇,这回答了我在SQLAlchemy部分的所有问题,甚至还添加了一些关于Flask和Pyramid的信息!额外奖励:开发人员回答;)我希望我能不止一次投票.非常感谢你! (16认同)
  • expire_on_commit可以导致更多SQL,如果你再次重复使用同一个Session,并且某些对象仍然在该Session中闲逛,当你访问它们时,你会得到每一个的单行SELECT,因为它们各自单独刷新他们在新交易方面的状态. (3认同)
  • 嗨,@zzzeek。感谢您的出色回答。我对 python 很陌生,我想澄清一些事情:1)当我通过调用 Session() 方法创建新的“会话”时,我理解是否正确,它将创建 SQL 事务,然后事务将被打开,直到我提交/回滚会话?2)session()是否使用某种连接池或每次都与sql建立新连接? (3认同)

Ber*_*pac 21

除了优秀的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()
Run Code Online (Sandbox Code Playgroud)

用法:

from mymodels import Foo

with db_session("sqlite://") as db:
    foos = db.query(Foo).all()
Run Code Online (Sandbox Code Playgroud)

  • 您为什么不仅创建一个新会话,而且还创建一个新连接,是有原因的? (2认同)