SQLAlchemy:创建与重用会话

时间:2012-08-31 22:46:25

标签: python sqlalchemy

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

from project import Session
session = Session()

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

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

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

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

2 个答案:

答案 0 :(得分:178)

sessionmaker()是一个工厂,它鼓励在一个地方放置用于创建新Session对象的配置选项。它是可选的,因为你可以随时轻松地调用Session(bind=engine, expire_on_commit=False),只要你需要一个新的Session,除了它的冗长和冗余,我想阻止小规模“助手”的扩散,每个人都以一种新的,更令人困惑的方式解决了这种冗余的问题。

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

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

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

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

答案 1 :(得分:17)

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

from mymodels import Foo

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

用法:

{{1}}