我确实了解,sqlalchemy.orm.scoping.scoped_session
使用session_factory
创建会话,并且还具有注册表以通过__call__()
调用返回已经存在的会话。
但是也可以直接在.query
上调用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 Documentation中描述了此行为:
隐式方法访问
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
只是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
实例时,这些方法已经被猴子修补了。关于python - 如何实现sqlalchemy.orm.scoping.scoped_session代理功能?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59972523/