在我正在处理的应用程序中,特定类的实例在其生命周期的 end 中保留,并且当它们不随后被修改时,它们的属性可能会需要阅读。例如,实例的end_time
或其相对于同一类的其他实例的序数位置(初始化的第一个实例获得值1,下一个实例具有值2等)。
class Foo(object):
def __init__(self, position):
self.start_time = time.time()
self.end_time = None
self.position = position
# ...
def finishFoo(self):
self.end_time = time.time()
self.duration = self.end_time - self.start_time
# ...
遵循我认为最佳做法 - 使用范围SQLAlchemy Session
,suggested here,contextlib.contextmanager
- 我将实例保存在新创建的{{} 1}}立即提交。下一行引用newly persistent实例,在日志记录中提及它,它会抛出Session
,因为当DetachedInstanceError
提交时,它引用的属性已过期。
Session
我知道我可以将class Database(object):
# ...
def scopedSession(self):
session = self.sessionmaker()
try:
yield session
session.commit()
except:
session.rollback()
logger.warn("blah blah blah...")
finally:
session.close()
# ...
def saveMyFoo(self, foo):
with self.scopedSession() as sql_session:
sql_session.add(foo)
logger.info("Foo number {0} finished at {1} has been saved."
"".format(foo.position, foo.end_time))
## Here the DetachedInstanceError is raised
标志设置为False来规避这个问题,但我担心这是一个值得怀疑的做法 - 自动过期存在是有原因的,我犹豫是否任意将所有问题全部归咎于ORM绑定的类没有充分的理由和理解背后的非到期状态。或者,我可以忘记对expire_on_commit
进行范围设定,并将事务保留为待处理状态,直到我(稍后)显式提交为止。
所以我的问题归结为:
Session
?Session
和ORM?当我排除随后引用任何持久属性的能力时,使用Session
方法似乎是矛盾的,即使对于像记录这样简单且广泛适用的任务也是如此。上面的例子被简化为专注于手头的问题,但如果它有用,这里是实际的精确追溯产生的。在contextmanager
调用中运行str.format()
时会出现此问题,该调用会尝试执行logger.debug()
实例的Set
方法。
__repr__()
答案 0 :(得分:3)
很有可能,是的。只要正确地将数据保存到数据库,就可以正确使用它。但是,由于您的事务仅跨越更新,因此在更新同一行时可能会遇到竞争条件。根据应用程序的不同,这可以。
不到期的属性是正确的方法。默认情况下到期的原因是因为它确保即使是天真的代码也能正常工作。如果你小心,那不应该是一个问题。
将交易概念与会话概念分开是很重要的。 contextmanager
做了两件事:它维护会话以及事务。每个ORM实例的生命周期仅限于每个事务的范围。这样您就可以假设对象的状态与数据库中相应行的状态相同。这就是框架在提交时使属性到期的原因,因为在事务提交后它不再能保证值的状态。因此,您只能在事务处于活动状态时访问实例的属性。
提交后,您访问的任何后续属性都将导致启动新事务,以便ORM可以再次保证数据库中值的状态。
但为什么会出错呢?这是因为您的会话已经消失,因此ORM无法启动事务。如果您在上下文管理器块中间执行session.commit()
,则在访问其中一个属性时,您会注意到正在启动的新事务。
那么,如果我只想访问以前获取的值,该怎么办?然后,您可以要求框架不要使这些属性失效。