温馨提示:本文翻译自stackoverflow.com,查看原文请点击:python - How is sqlalchemy.orm.scoping.scoped_session proxying ability implemented?

python - 如何实现sqlalchemy.orm.scoping.scoped_session代理功能?

发布于 2020-03-30 21:39:25

我确实知道,a sqlalchemy.orm.scoping.scoped_session使用a session_factory来创建会话,并且还拥有注册表以通过__call__()调用返回已经存在的会话但是,也可以直接调用.query方法scoped_session,这完全使我感到困惑,因为scoped_session:1.没有此方法2.不是a的动态包装,sqlalchemy.orm.session.Session并且3.不是a的子类sqlalchemy.orm.session.Session

如何scoped_session调度查询?我只是看不到任何允许这种方式的间接或抽象。

from sqlalchemy.orm import sessionmaker,scoped_session
from sqlalchemy import create_engine

user, password, server, dbname = "123","123","123", "123"
s = 'oracle://%s:%s@%s/%s' % (user, password, server, dbname)
some_engine = create_engine(s)
_sessionmaker = sessionmaker(bind=some_engine)
sc_sess = scoped_session(_sessionmaker) # here sc_sess is an isntance of "sqlalchemy.orm.scoping.scoped_session"
sc_sess.query(...) # works! but why?

# the following is what i expect to work and to be normal workflow
session = sc_sess() # returns an instance of sqlalchemy.orm.session.Session
session.query(...)

SqlAlchemy 文档中描述了此行为

隐式方法访问

scoped_session的工作很简单;为所有要求的人举行会议。作为产生对该会话的更透明访问的一种方式,scoped_session还包括代理行为,这意味着注册表本身可以直接像Session一样被对待。在此对象上调用方法时,它们将代理到注册表维护的基础会话:

Session = scoped_session(some_factory)

# equivalent to:
#
# session = Session()
# print(session.query(MyClass).all())
#
print(Session.query(MyClass).all())

上面的代码通过调用注册表,然后使用该会话来完成与获取当前会话相同的任务。

因此,这种行为是正常的,但是如何实现呢?(通常不是代理,但在此示例中正是)

谢谢。

查看更多

提问者
Roger Smith
被浏览
38
SuperShoot 2020-01-31 18:17

您显然已经很好地了解了sqlalchemy.orm.scoping.scoped_session该类,并且如果您在同一模块中稍稍向下看,您会发现以下代码片段(link):

def instrument(name):
    def do(self, *args, **kwargs):
        return getattr(self.registry(), name)(*args, **kwargs)

    return do


for meth in Session.public_methods:
    setattr(scoped_session, meth, instrument(meth))

如果我们从头到尾进行剖析,我们首先会得到一个for meth in Session.public_methods:循环,这里Session.public_methods只是一个a Session公开的方法名称的元组,而字符串"query"就是其中之一:

class Session(_SessionClassMethods):
    ...
    public_methods = (
        ...,
        "query",
        ...,
    )

其中的每个名称(methSession.public_methods都会传递给setattr循环内的调用:

setattr(scoped_session, meth, instrument(meth))

在上分配给方法名称的scoped_session值是对的调用的返回值instrument(meth),这是一个称为的闭包do()该函数调用scoped_session.registry来获取注册的Session对象,获取命名方法(meth),然后使用传递给*args**kwargs进行调用do()

由于for meth in Session.public_methods:循环是在模块的全局命名空间中定义的,因此在其他任何机会都不能使用的情况下,在编译时执行循环scoped_session因此,当您的代码可以拥有一个scoped_session实例时,这些方法已经被猴子修补了。