如何使用session.merge()处理sqlalchemy ObjectDeletedError并删除表
我有一个使用session.merge()创建并定期更新的表行。如果数据库(sqlite)中的表被删除,那么我会得到一个ObjectDeletedError,但是我找不到一种方法来优雅地处理这个问题 在异常时,我重新创建表,但异常仍然发生 我需要做什么才能让会话识别新表、创建行并像以前一样继续 这是代码如何使用session.merge()处理sqlalchemy ObjectDeletedError并删除表,merge,sqlalchemy,Merge,Sqlalchemy,我有一个使用session.merge()创建并定期更新的表行。如果数据库(sqlite)中的表被删除,那么我会得到一个ObjectDeletedError,但是我找不到一种方法来优雅地处理这个问题 在异常时,我重新创建表,但异常仍然发生 我需要做什么才能让会话识别新表、创建行并像以前一样继续 这是代码 #!/usr/bin/env python import sqlalchemy from sqlalchemy.ext.declarative import declarative_base
#!/usr/bin/env python
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
##============================================================================
class Module(Base):
"""Schema for the modules table."""
##------------------------------------------------------------------------
__tablename__ = "modules"
id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
port = sqlalchemy.Column(sqlalchemy.Integer)
##------------------------------------------------------------------------
def __init__(self, id, port=None):
self.id = id
self.port = port
##============================================================================
class SessionMgr(object):
"""Class to manage the database session."""
##------------------------------------------------------------------------
def __init__(self, commitInterval=5, debug=False):
self.commitInterval = commitInterval
database = "merge-bug.db"
self.engine = sqlalchemy.create_engine("sqlite:///%s" % (database), echo=debug)
Session = sessionmaker(bind=self.engine)
self.session = Session()
self.create_tables()
##------------------------------------------------------------------------
def create_tables(self):
"""Create database tables if they do not exist."""
### List of tables to check/create.
tables = [ Module ]
### Create each table if it does not exist.
for t in tables:
tname = t.__tablename__
if not self.engine.dialect.has_table(self.engine.connect(), tname):
print "%s table didn't exist. Creating..." % (tname)
t.metadata.create_all(self.engine)
##------------------------------------------------------------------------
def commit(self):
"""Commit changes to the database."""
print "Committing to db"
try:
self.session.commit()
except Exception as ex:
print "ERROR: SessionMgr.commit():", ex
print "DEBUG: SessionMgr.commit(): Issuing a session rollback ..."
self.session.rollback()
## Check that tables still exist.
self.create_tables()
finally:
## Optional -- depends on use case.
#self.session.remove()
pass
##------------------------------------------------------------------------
##============================================================================
def main():
print "DEBUG: main():"
import time
sessmgr = SessionMgr(commitInterval=5, debug=False)
##
## Test adding a module and updating it using session.merge().
##
if 1:
errors = 0
m = Module(1234, 43210)
print "DEBUG: merge module"
mm = sessmgr.session.merge(m)
sessmgr.commit()
delay = 1
for i in range(10):
try:
print "DEBUG: sleeping %i second ..." % (delay)
time.sleep(delay)
port = mm.port + 1
print "DEBUG: updating port field to %i ..." % (port)
## Exception on the following statement if table is deleted !!
mm.port = port
sessmgr.commit()
print "DEBUG: updating id field ..."
mm.id = 1234
sessmgr.commit()
except Exception as ex:
print "DEBUG: caught exception", ex
if 0:
print "DEBUG: Issuing a session rollback ..."
sessmgr.session.rollback()
if 1:
print "DEBUG: Create tables ..."
sessmgr.create_tables()
if 1:
print "DEBUG: Remerge module ..."
m = Module(1234, 43210)
mm = sessmgr.session.merge(mm)
if 0:
print "DEBUG: Refresh merged module ..."
mm = sessmgr.session.merge(mm)
errors += 1
print "DEBUG: errors =", errors
if errors > 3:
raise
##============================================================================
if __name__ == "__main__":
main()
##============================================================================
这是输出
DEBUG: main():
DEBUG: merge module
Committing to db
DEBUG: sleeping 2 second ...
DEBUG: updating port field to 43211 ...
Committing to db
DEBUG: updating id field ...
Committing to db
DEBUG: sleeping 2 second ...
DEBUG: caught exception (OperationalError) no such table: modules u'SELECT modules.id AS modules_id, modules.port AS modules_port \nFROM modules \nWHERE modules.id = ?' (1234,)
DEBUG: Create tables ...
modules table didn't exist. Creating...
Committing to db
DEBUG: Remerge module ...
DEBUG: errors = 1
DEBUG: sleeping 2 second ...
DEBUG: caught exception Instance '<Module at 0x102a586d0>' has been deleted, or its row is otherwise not present.
DEBUG: Create tables ...
Committing to db
DEBUG: Remerge module ...
DEBUG: errors = 2
DEBUG: sleeping 2 second ...
DEBUG: caught exception Instance '<Module at 0x102a586d0>' has been deleted, or its row is otherwise not present.
DEBUG: Create tables ...
Committing to db
DEBUG: Remerge module ...
DEBUG: errors = 3
DEBUG: sleeping 2 second ...
DEBUG: caught exception Instance '<Module at 0x102a586d0>' has been deleted, or its row is otherwise not present.
DEBUG: Create tables ...
Committing to db
DEBUG: Remerge module ...
DEBUG: errors = 4
Traceback (most recent call last):
File "merge_bug.py", line 135, in <module>
main()
File "merge_bug.py", line 103, in main
port = mm.port + 1
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/sqlalchemy/orm/attributes.py", line 316, in __get__
return self.impl.get(instance_state(instance), dict_)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/sqlalchemy/orm/attributes.py", line 611, in get
value = callable_(passive)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/sqlalchemy/orm/state.py", line 375, in __call__
self.manager.deferred_scalar_loader(self, toload)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/sqlalchemy/orm/loading.py", line 606, in load_scalar_attributes
raise orm_exc.ObjectDeletedError(state)
sqlalchemy.orm.exc.ObjectDeletedError: Instance '<Module at 0x102a586d0>' has been deleted, or its row is otherwise not present.
DEBUG:main():
调试:合并模块
向数据库提交
调试:睡眠2秒。。。
调试:正在将端口字段更新为43211。。。
向数据库提交
调试:正在更新id字段。。。
向数据库提交
调试:睡眠2秒。。。
调试:捕获异常(操作错误)没有这样的表:modules u'选择modules.id作为modules\u id,modules.port作为modules\u port\n从modules\n此处modules.id=?'(1234,)
调试:创建表。。。
模块表不存在。创建。。。
向数据库提交
调试:重新合并模块。。。
调试:错误=1
调试:睡眠2秒。。。
调试:捕获的异常实例“”已被删除,或者其行不存在。
调试:创建表。。。
向数据库提交
调试:重新合并模块。。。
调试:错误=2
调试:睡眠2秒。。。
调试:捕获的异常实例“”已被删除,或者其行不存在。
调试:创建表。。。
向数据库提交
调试:重新合并模块。。。
调试:错误=3
调试:睡眠2秒。。。
调试:捕获的异常实例“”已被删除,或者其行不存在。
调试:创建表。。。
向数据库提交
调试:重新调整模块。。。
调试:错误=4
回溯(最近一次呼叫最后一次):
文件“merge_bug.py”,第135行,在
main()
文件“merge_bug.py”,第103行,在main中
端口=毫米。端口+1
文件“/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site packages/sqlalchemy/orm/attributes.py”,第316行,在__
返回self.impl.get(实例\状态(实例),dict\状态)
get中的文件“/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site packages/sqlalchemy/orm/attributes.py”,第611行
值=可调用(被动)
文件“/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site packages/sqlalchemy/orm/state.py”,第375行,在调用中__
self.manager.deferred\u标量\u加载器(self,toload)
文件“/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site packages/sqlalchemy/orm/loading.py”,第606行,在load\u scalar\u属性中
提升orm_exc.ObjectDeletedError(状态)
sqlalchemy.orm.exc.ObjectDeletedError:实例“”已被删除,或者其行不存在。
我需要做什么才能让会话识别新表、创建行并像以前一样继续
当会话发生错误时,您需要回滚该会话以取消当前事务:
session.rollback()
现在,如果您仍然存在此问题,那么SQLite可能无法处理被删除的表(我假设是通过另一个连接?),您可能需要完全重新连接到数据库-每次会话关闭其事务时,使用NullPool
禁用连接池将实现这一点,连接本身将被关闭
在应用程序运行时,创建和删除表实际上也不适合使用关系数据库。如果需要删除表中的所有数据,请使用“从表中删除”。当应用程序运行时(特别是在像SQLite这样脆弱的系统上),表结构本身不应该改变。session.rollback()不能解决这个问题。我现在也在mysql上进行了测试,它也做了同样的事情。如果使用“从表中删除;”,也会发生此错误。我确信一定有办法从这种情况中恢复过来(即使它永远不会发生)。我只是按原样运行了你的程序,它运行得很好,所以我想在同一时间还需要发生其他事情?是的。在程序运行几秒钟后,在另一个终端中键入:“echo-e”drop table modules;“|sqlite3”。这将删除该表,并出现异常。“从模块中删除”也会发生同样的情况