我确实理解,sqlalchemy.orm.scoping.scoped_session使用一个session_factory来创建一个会话,并且拥有一个注册中心来通过__call__()调用返回一个已经存在的会话。但是我们也可以直接在scoped_session上调用scoped_session方法,这完全让我感到困惑,因为scoped_session: 1.没有这个方法。2不是sqlalchemy.orm.session.Session的动态包装器,3不是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 = scoped_session(some_factory)
# equivalent to:
#
# session = Session()
# print(session.query(MyClass).all())
#
print(Session.query(MyClass).all())上面的代码通过调用注册表来完成与获取当前会话的任务相同的任务,然后使用该会话。
所以这种行为是正常的,但是它是如何实现的呢?(一般情况下不是代理,但在本例中正是如此)
谢谢。
发布于 2020-01-31 10:17:08
显然,您已经很好地了解了sqlalchemy.orm.scoping.scoped_session类,如果您在同一个模块中再往下看一点,您会发现下面的片段(链接):
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只是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),并使用传递给do()的*args & **kwargs调用它。
由于for meth in Session.public_methods:循环是在模块的全局命名空间中定义的,它在编译时执行,然后才有机会使用scoped_session。因此,当您的代码能够获得scoped_session实例时,这些方法已经在那里进行了修补。
https://stackoverflow.com/questions/59972523
复制相似问题