SQLAlchemy+Tornado: 如何为SQLAlchemy的ScopedSession创建scopefunc?

7 投票
2 回答
3254 浏览
提问于 2025-04-17 09:14

我在用tornado框架的时候,想要做一些中间件的处理,确保我的SQLAlchemy会话能够正确关闭和清理,这样就不会让一个请求中的对象被下一个请求共享。问题是,因为我的一些tornado处理器是异步的,所以我不能简单地为每个请求共享一个会话。

因此,我需要创建一个ScopedSession,它能够为每个请求创建一个新的会话。我只需要为我的代码定义一个scopefunc,这个函数可以把当前正在执行的请求变成某种独特的标识符。不过,我似乎无法在任何时刻获取到当前请求(在当前RequestHandler的范围之外,我的函数也无法访问)。

有没有什么办法可以让我实现这个功能呢?

2 个回答

0

(这是2017年对2011年问题的回答)正如@Stefano Borini所指出的,在Tornado 4中,最简单的方法就是让RequestHandler自动“传递”会话。Tornado会在使用协程装饰器时跟踪处理器实例的状态:

import logging

_logger = logging.getLogger(__name__)

from sqlalchemy import create_engine, exc as sqla_exc
from sqlalchemy.orm import sessionmaker, exc as orm_exc

from tornado import gen
from tornado.web import RequestHandler

from my_models import SQLA_Class

Session = sessionmaker(bind=create_engine(...))

class BaseHandler(RequestHandler):

    @gen.coroutine
    def prepare():
        self.db_session = Session()

    def on_finish():
        self.db_session.close()

class MyHander(BaseHandler):

    @gen.coroutine
    def post():
        SQLA_Object = self.db_session.query(SQLA_Class)...
        SQLA_Object.attribute = ...

        try:
            db_session.commit()
        except sqla_exc.SQLAlchemyError:
            _logger.exception("Couldn't commit")
            db_session.rollback()

如果你真的非常需要在declarative_base中异步引用一个SQL Alchemy会话(我认为这样做是不太好的,因为它会让模型和应用程序过于紧密地耦合在一起),Amit Matani在这里提供了一个不工作的例子。

6

你可能想把Session和请求本身关联起来(也就是说,如果不方便的话就不要使用scopedsession)。这样你只需要用request.session就可以了。不过还是需要在开始和结束的时候设置一些东西来准备和清理。

补充:自定义作用域函数

def get_current_tornado_request():
   # TODO: ask on the Tornado mailing list how
   # to acquire the request currently being invoked

Session = scoped_session(sessionmaker(), scopefunc=get_current_tornado_request)

撰写回答