多线程sqlalchemy webapp中推荐的scoped_session使用模式是什么?

Luk*_*404 27 python session multithreading sqlalchemy

我正在用python和sqlalchemy-0.7编写一个应用程序.它首先初始化sqlalchemy orm(使用声明),然后启动多线程Web服务器 - 我目前正在使用web.py进行快速原型设计,但将来可能会发生变化.我还将为预定作业添加其他"线程"等等,可能使用其他python线程.

从SA文档我明白我必须使用scoped_session()来获得线程本地会话,所以我的web.py应用程序最终应该看起来像:

import web
from myapp.model import Session  # scoped_session(sessionmaker(bind=engine))
from myapp.model import This, That, AndSoOn
urls = blah...
app  = web.application(urls, globals())

class index:
    def GET(self):
        s = Session()
        # get stuff done
        Session().remove()
        return(stuff)

class foo:
    def GET(self):
        s = Session()
        # get stuff done
        Session().remove()
        return(stuff)
Run Code Online (Sandbox Code Playgroud)

这是处理会话的正确方法吗?

据我所知,我应该在每个方法都得到一个scoped_session,因为它会给我一个我事先无法获得的线程本地会话(比如在模块级别).

此外,我应该在每个方法结束时调用.remove()或.commit()或类似的东西,否则会话仍将包含Persistent对象,我将无法查询/访问其他线程中的相同对象?

如果那个模式是正确的,那么通过只编写一次,也许使用装饰器可能会使它变得更好?这样的装饰器可以获取会话,调用方法然后确保正确地处理会话.如何将会话传递给装饰函数?

Ale*_*okk 21

是的,这是正确的方法.

例:

使用Flask-sqlalchemy扩展的Flask框架可以实现您所描述的功能.它还在每个HTTP请求("视图"函数)结束时自动执行.remove(),因此会话由当前线程释放.仅调用.commit()是不够的,你应该使用.remove().

不使用Flask视图时,我通常使用"with"语句:

@contextmanager
def get_db_session():
    try:
        yield session
    finally:
        session.remove()

with get_db_session() as session:
    # do something with session
Run Code Online (Sandbox Code Playgroud)

您可以创建一个类似的装饰器.

Scoped会话创建一个DBMS连接池,因此这种方法比每个HTTP请求的打开/关闭会话更快.它也适用于greenlets(gevent或eventlet).

  • 我认为这个例子应该是`get_db_session()作为session:`,或者你不能不使用session. (2认同)