具有多个会话的 SQLAlchemy,如何避免 "Object is already attached to session X"?
SQLAlchemy with multiple session, how to avoid "Object is already attached to session X"?
我有一个特殊的配置,我从 sessionmaker 创建了两个 SQLAlchemy 会话:
db.read = sessionmaker(autoflush=False)
db.write = sessionmaker()
调用db.write.add(obj)
时,我有时会出现以下错误(但并非总是如此):
sqlalchemy.exc.InvalidRequestError: Object '<User at 0x7f3fa9ebee50>' is already attached to session '14' (this is '15')
有没有办法做到以下几点:
"如果session是db.write,那么调用db.write.add(obj)
否则,将 obj 添加到 db.write 会话以保存它,并且(重要)更新 db.write 会话和 db.read 会话中的对象
(我已经设法通过写入保存到,但是 obj.id 在 db.read 中是空的)
感谢您的帮助
我们可以通过扩展 Session.add
来检测和处理对象已经在另一个会话中的情况,并通过添加事件监听器来在对象已经存在时将对象恢复到读取会话中来近似期望的行为被承诺。一个对象不可能同时存在于两个会话中:如果该对象最好保留在写入会话中,则不需要侦听器和 _prev_session
属性。
import weakref
import sqlalchemy as sa
from sqlalchemy import orm
...
class CarefulSession(orm.Session):
def add(self, object_):
# Get the session that contains the object, if there is one.
object_session = orm.object_session(object_)
if object_session and object_session is not self:
# Remove the object from the other session, but keep
# a reference so we can reinstate it.
object_session.expunge(object_)
object_._prev_session = weakref.ref(object_session)
return super().add(object_)
# The write session must use the Session subclass; the read
# session need not.
WriteSession = orm.sessionmaker(engine, class_=CarefulSession)
@sa.event.listens_for(WriteSession, 'after_commit')
def receive_after_commit(session):
"""Reinstate objects into their previous sessions."""
objects = filter(lambda o: hasattr(o, '_prev_session'), session.identity_map.values())
for object_ in objects:
prev_session = object_._prev_session()
if prev_session:
session.expunge(object_)
prev_session.add(object_)
delattr(object_, '_prev_session')
完整的实现可能会添加一个 after_rollback
侦听器,并且可能会在基本模型的 __init__
中初始化 _prev_session
以避免 del/has attr 调用。
我有一个特殊的配置,我从 sessionmaker 创建了两个 SQLAlchemy 会话:
db.read = sessionmaker(autoflush=False)
db.write = sessionmaker()
调用db.write.add(obj)
时,我有时会出现以下错误(但并非总是如此):
sqlalchemy.exc.InvalidRequestError: Object '<User at 0x7f3fa9ebee50>' is already attached to session '14' (this is '15')
有没有办法做到以下几点:
"如果session是db.write,那么调用db.write.add(obj) 否则,将 obj 添加到 db.write 会话以保存它,并且(重要)更新 db.write 会话和 db.read 会话中的对象
(我已经设法通过写入保存到,但是 obj.id 在 db.read 中是空的)
感谢您的帮助
我们可以通过扩展 Session.add
来检测和处理对象已经在另一个会话中的情况,并通过添加事件监听器来在对象已经存在时将对象恢复到读取会话中来近似期望的行为被承诺。一个对象不可能同时存在于两个会话中:如果该对象最好保留在写入会话中,则不需要侦听器和 _prev_session
属性。
import weakref
import sqlalchemy as sa
from sqlalchemy import orm
...
class CarefulSession(orm.Session):
def add(self, object_):
# Get the session that contains the object, if there is one.
object_session = orm.object_session(object_)
if object_session and object_session is not self:
# Remove the object from the other session, but keep
# a reference so we can reinstate it.
object_session.expunge(object_)
object_._prev_session = weakref.ref(object_session)
return super().add(object_)
# The write session must use the Session subclass; the read
# session need not.
WriteSession = orm.sessionmaker(engine, class_=CarefulSession)
@sa.event.listens_for(WriteSession, 'after_commit')
def receive_after_commit(session):
"""Reinstate objects into their previous sessions."""
objects = filter(lambda o: hasattr(o, '_prev_session'), session.identity_map.values())
for object_ in objects:
prev_session = object_._prev_session()
if prev_session:
session.expunge(object_)
prev_session.add(object_)
delattr(object_, '_prev_session')
完整的实现可能会添加一个 after_rollback
侦听器,并且可能会在基本模型的 __init__
中初始化 _prev_session
以避免 del/has attr 调用。