如何使用session.merge()处理sqlalchemy ObjectDeletedError并删除表

时间:2013-06-17 03:22:59

标签: merge sqlalchemy

我有一个使用session.merge()创建并定期更新的表行。如果DB(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.

1 个答案:

答案 0 :(得分:0)

  

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

当Session发生错误时,您需要将其回滚以取消当前事务:

session.rollback()

现在如果您仍然遇到此问题,那么SQLite可能无法处理被删除的表(我假设是通过不同的连接?)您可能需要完全重新连接到数据库 - 禁用连接池使用NullPool将实现此目的,每次Session关闭其事务时,连接本身都将被关闭。

在应用程序运行时,关键数据库也不适合使用创建和删除表。如果需要删除表中的所有数据,请使用&#34; DELETE FROM table&#34;。应用程序运行时,表结构本身不应该更改(特别是在像SQLite这样的脆弱系统上)。