在SQLAlchemy中使用scoped_session(sessionmaker())还是普通的sessionmaker()?

73 投票
5 回答
54070 浏览
提问于 2025-04-16 20:30

我在我的网页项目中使用SQLAlchemy。我应该用scoped_session(sessionmaker())还是普通的sessionmaker(),为什么?或者我应该用别的什么吗?

## model.py
from sqlalchemy import *
from sqlalchemy.orm import *

engine = create_engine('mysql://dbUser:dbPassword@dbServer:dbPort/dbName',
pool_recycle=3600, echo=False)
metadata = MetaData(engine)
Session = scoped_session(sessionmaker())
Session.configure(bind=engine)
user = Table('user', metadata, autoload=True)

class User(object):
pass

usermapper = mapper(User, user)

## some other python file called abc.py
from models import *

def getalluser():
   session = Session()  
   session.query(User).all()
   session.flush()
   session.close()

## onemore file defg.py
from models import *

def updateuser():
   session = Session()  
   session.query(User).filter(User.user_id == '4').update({User.user_lname: 'villkoo'})
   session.commit()
   session.flush()
   session.close()

我为每个请求创建一个session = Session()对象,然后关闭它。这样做对吗?还是有更好的方法?

5 个回答

12

不要使用scoped_session,也不要使用Flask-SQLAlchemy。

直接使用 Session = sessionmaker(),把它放在一个单例或服务类里,然后在每个HTTP请求中使用 session = Session(),这样可以确保每次都能提供一个新的连接。

线程本地存储比较麻烦,它需要保存状态,这在不同的网络服务器线程模型中不太好用。最好保持无状态。比如,SqlAlchemy的文档提到,如果你在使用 scoped_session,不要忘记调用 .remove()。会有人记得这样做吗?

下面是来自 https://docs.sqlalchemy.org/en/14/orm/contextual.html#using-thread-local-scope-with-web-applications 的一段摘录:

按照上述流程,将Session与网络应用集成有两个要求:

  • 在网络应用首次启动时创建一个单一的 scoped_session 注册表,确保这个对象可以被应用的其他部分访问。

  • 确保在网络请求结束时调用 scoped_session.remove(),通常通过与网络框架的事件系统集成来建立一个“请求结束”事件。

12

在每个方法里使用Scoped_session会给你一个本地的会话,这个会话是你不能提前获取的(比如在模块级别)。其实在每个方法里都打开一个新会话并不是必要的,你可以使用一个全局会话。只有在全局会话不可用的时候,才创建一个新的会话。也就是说,你可以写一个方法来返回一个会话,并把它添加到你的包里的init.py文件中。

58

建议阅读文档

scoped_session()这个函数可以创建一个线程管理的会话对象注册表。它通常在网页应用中使用,这样就可以用一个全局变量安全地表示与一组对象相关的事务会话,而且这些会话是局限在单个线程中的。

简单来说,使用scoped_session()可以确保线程安全。

撰写回答