Как реализована возможность проксирования sqlalchemy.orm.scoping.scoped_session? - PullRequest
1 голос
/ 29 января 2020

Я понимаю, что sqlalchemy.orm.scoping.scoped_session использует session_factory для создания сеанса, а также обладает реестром для возврата уже существующего сеанса через вызов __call__(). Но можно также напрямую вызывать метод .query для scoped_session, и это меня полностью смущает, поскольку scoped_session: 1. не имеет этого метода 2. не является оболочкой Dynami c для 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())

Приведенный выше код выполняет ту же задачу, что и получение текущего сеанса вызывая реестр, затем используя этот сеанс.

Итак, это нормальное поведение, но как оно реализовано? (не прокси в целом, но именно в этом примере)

Спасибо.

1 Ответ

1 голос
/ 31 января 2020

Вы, очевидно, хорошо ознакомились с классом 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: l oop, где Session.public_methods просто кортеж имен методов, предоставляемых Session, и строка "query" является одной из них:

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

Каждое из этих имен (meth) в Session.public_methods передается в setattr вызов внутри l oop:

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: l oop определено в глобальном пространстве имен модуля, оно выполняется во время компиляции, прежде чем что-либо еще сможет использовать scoped_session. Таким образом, к тому моменту, когда ваш код сможет овладеть экземпляром scoped_session, эти методы уже были исправлены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...