Отключение экземпляра SQLAlchemy, поэтому обновление не происходит

Я хочу отделить экземпляр класса от моего сеанса, но он все равно должен быть доступен для чтения (без испускания запроса). Я просматривал документацию в течение нескольких дней, но каждый подход, который я пытаюсь, приводит к сообщению

DetachedInstanceError: Instance <MyModel at 0x36bb190> is not bound to a Session;
attribute refresh operation cannot proceed

Я работаю с менеджером транзакций zope.sqlalchemy в Pyramid. Я хочу, чтобы мой объект был доступен после, транзакция была совершена. Мне нужно только его прочитать "кэшированное" значение, то есть те, которые были в нем до совершения транзакции.

Единственный способ, которым я мог понять, - это обернуть класс (или сами атрибуты), а затем отслеживать изменения вручную (я мог бы это сделать, но это действительно уродливо и вовсе не pythonic).

Итак, есть ли способ предотвратить попытку SQLAlchemy обновить эти значения?

В качестве резервной копии я даже был бы открыт только для возврата None, если вышеприведенная ошибка не будет выбрана после совершения транзакции

Ответ 1

http://docs.sqlalchemy.org/en/latest/orm/session_api.html

Я думаю, что вы ищете expire_on_commit = False

Я считаю, что это позволяет вам отсоединить объект и продолжать его использовать. Однако попытка изменить его и зафиксировать приведет к DetachedInstanceError.

Ответ 2

Вы можете сделать именно это (например, для кеширования), выполнив:

session.expunge(obj)

Согласно документации sqlalchemy:

http://docs.sqlalchemy.org/en/rel_1_0/orm/session_api.html?highlight=expire#sqlalchemy.orm.session.Session.expunge

Это даст вам объект, который находится в состоянии отсоединения, который вы можете безопасно использовать - вам нужно помнить, что вы не сможете читать свойства, которые могли бы испускать другой запрос, привязанный таким образом к отношениям, подобным сеансу, в результате это будет DetachedInstanceError.

По умолчанию commit будет выдавать expire_all(), что означает, что все объекты будут обновлять свое состояние при чтении, исключив их, вы отделите их от сеанса, чтобы после выполнения транзакции не было последующих запросов.

Я бы посоветовал не отключать эту функциональность глобально, поскольку другие комментарии предполагают, что Майк Байер обычно предлагает, что это хорошая идея и нормальный по умолчанию для большинства людей, которые могут сэкономить вам головные боли в долгосрочной перспективе.

Просто удаляйте вещи, когда они вам понадобятся.

Ответ 3

попробуйте следующее:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(), expire_on_commit=False))

У меня тоже была эта проблема, и с помощью expire_on_commit=False я решил проблему.

Ответ 4

@contextmanager
def make_session_scope(Session):
    """Provide a transactional scope around a series of operations."""
    session = Session()
    session.expire_on_commit = False
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

 with make_session_scope(session) as session:
      query = session.query(model)