Отключение экземпляра 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)