会话关闭后,Reading DateTime列给出DetachedInstanceError



我想访问映射对象上名为timeCreated的字段。该字段是通过utcnow()FunctionElement(取自本例(实例化的。

在执行mergeadd调用、提交然后关闭会话之后,我注意到在访问该字段时出现以下错误。我已将expire_on_commit设置为False。

sqlalchemy.orm.exc.DetachedInstanceError: Instance <User at 0x102046710> is not bound to a Session; attribute refresh operation cannot proceed (Background on this error at: http://sqlalche.me/e/13/bhk3)

示例代码:

def write(obj):
with sessionScope() as session:
obj.timeCreated = utcnow()
ret = session.merge(obj)
return ret
user = User(name='Totoro')
savedUser = write(user)
# Error occurs when accessing timeCreated
print(savedUser.timeCreated)

SessionScope((取自这些文档,它被定义为:

sessionFactory = sessionmaker(bind=engine)
@contextmanager
def sessionScope():
try:
session = sessionFactory()
yield session
session.commit()
except Exception as e:
session.rollback()
raise
finally:
session.close()
return

commit()之后,timeCreated没有被解析是有原因的吗?如果在提交之后但在关闭之前,我访问timeCreated,那么close之后的后续访问仍然有效。

有没有办法";热切的";加载这种类型的列?

问题是,当timeCreated被分配给调用utcnow的结果时,SQLAlchemy不会立即从服务器分配结果;相反,会分配一个占位符值,正如我们在调试器中看到的那样:

(Pdb) obj.__dict__
{..., 'timeCreated': <__main__.utcnow at 0x7f237d0a90a0; utcnow object>}

当会话关闭时,此占位符值将过期:

(Pdb) sa.inspect(savedUser).expired_attributes
{'timeCreated'}

因此,如问题末尾所述,必须在会话关闭之前加载timeCreated的值,以防止以后访问DetachedInstanceError

根据Fetching Server Generated Defaults(案例1(的文档,可以通过将timeCreatedserver_default属性设置为FetchedValue,并在映射器参数中将eager_defaults设置为True来完成此操作。

以下是一个示例模型(在Mariadb和Postgresql上测试(:

from sqlalchemy.schema import FetchedValue

class User(Base):
...
timeCreated = Column(DateTime, server_default=FetchedValue())
__mapper_args__ = {'eager_defaults': True}

值得一提的是,将server_default=utcnow()与映射器参数组合设置也同样有效,并且避免了显式设置timeCreated的需要;但也许OP有自己的理由这么做。

最新更新