如何使用session.merge()处理sqlalchemy 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

我有一个使用session.merge()创建并定期更新的表行。如果数据库(sqlite)中的表被删除,那么我会得到一个ObjectDeletedError,但是我找不到一种方法来优雅地处理这个问题

在异常时,我重新创建表,但异常仍然发生

我需要做什么才能让会话识别新表、创建行并像以前一样继续

这是代码

#!/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”。这将删除该表,并出现异常。“从模块中删除”也会发生同样的情况