我确实知道,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())
上面的代码通过调用注册表,然后使用该会话来完成与获取当前会话相同的任务。
因此,这种行为是正常的,但是如何实现呢?(通常不是代理,但在此示例中正是)
谢谢。
您显然已经很好地了解了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",
...,
)
其中的每个名称(meth
)Session.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
实例时,这些方法已经被猴子修补了。
不敢相信我已经监督了!:D非常感谢您,SuperShoot。