SQLAlchemy+Tornado: 如何为SQLAlchemy的ScopedSession创建scopefunc?
我在用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)