【问题标题】:SQLAlchemy+Tornado: How to create a scopefunc for SQLAlchemy's ScopedSession?SQLAlchemy+Tornado:如何为 SQLAlchemy 的 ScopedSession 创建一个 scopefunc?
【发布时间】:2011-12-30 00:30:40
【问题描述】:

使用 tornado,我想创建一些中间件魔术,以确保我的 SQLAlchemy 会话正确关闭/清理,以便对象不会从一个请求共享到下一个请求。诀窍在于,由于我的一些龙卷风处理程序是异步的,我不能只为每个请求共享一个会话。

所以我试图创建一个 ScopedSession,它知道如何为每个请求创建一个新会话。我需要做的就是为我的代码定义一个范围函数,它可以将当前执行的请求转换为某种唯一键,但是我似乎无法弄清楚如何 get 当前请求任何一个时间点(在当前 RequestHandler 的范围之外,我的函数也无权访问)。

我可以做些什么来完成这项工作吗?

【问题讨论】:

  • 我根本不知道龙卷风,但您可能希望将 Session 与请求本身相关联(即,如果不方便,请不要使用 scopedsession)。那么你可以说request.session。仍然需要在开始/结束时设置/拆卸钩子。
  • @zzzeek 如果您将此作为答案发布,我会将其标记为正确!我想得越多,我就越意识到你是对的——这是获得我需要的最快和最容易理解的方式。谢谢!
  • 我也有同样的想法,在所有异步处理程序之间共享通过 scopped_session 创建的会话会造成不一致,对吧?假设在一个处理程序中,我可能会在处理程序结束时调用 scopped_session.remove() 但其他处理程序(异步运行)可能仍在使用它!

标签: python sqlalchemy tornado


【解决方案1】:

您可能希望将 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)

【讨论】:

  • 所以基本上我需要做的是在请求开始时创建一个会话并将其分配给请求处理程序。但我需要调用许多需要会话的方法,我需要在它们之间传递请求处理程序或会话本身......代码变得丑陋:(
  • 然后使用具有自定义作用域功能的作用域会话。
  • 您能否详细说明如何编写自定义范围函数...谢谢。我是 Python 新手
  • tornado(或您的应用程序)需要提供一些函数,当不带参数调用时,返回当前正在服务的请求。然后你将它传递给“scopefunc”。请参阅上面的编辑。
  • 我不明白,你为什么不能在 prepare() 中开始会话?
【解决方案2】:

(这是对 2011 年问题的 2017 年回答)正如@Stefano Borini 指出的那样,Tornado 4 中最简单的方法是让 RequestHandler 隐式地使用pass the session around。 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 有一个无效的示例 @ 987654322@.

【讨论】:

    猜你喜欢
    • 2012-01-07
    • 2013-02-01
    • 1970-01-01
    • 2011-02-17
    • 2016-08-13
    • 1970-01-01
    • 2013-10-11
    • 1970-01-01
    • 2018-02-28
    相关资源
    最近更新 更多