尝试使用SQLAlchemy捕获完整性错误

时间:2012-07-03 15:12:21

标签: python sqlalchemy pyramid

我遇到了尝试捕获错误的问题。我正在使用Pyramid / SQLAlchemy并使用电子邮件作为主键创建了一个注册表单。问题是当输入重复的电子邮件时会引发IntegrityError,所以我试图捕获该错误并提供一条消息,但无论我做什么我都抓不到它,错误一直出现。

try:
    new_user = Users(email, firstname, lastname, password)
    DBSession.add(new_user)
    return HTTPFound(location = request.route_url('new'))
except IntegrityError:
    message1 = "Yikes! Your email already exists in our system. Did you forget your password?"

当我尝试except exc.SQLAlchemyError时,我收到相同的消息(虽然我想捕捉到特定的错误而不是全部捕获)。我也试过exc.IntegrityError但没有运气(虽然它存在于API中)。

我的Python语法是否有问题,或者我需要在SQLAlchemy中做些什么来捕获它?


我不知道如何解决这个问题,但我对可能导致问题的原因有一些想法。也许try语句没有失败但是成功,因为SQLAlchemy本身就引发了异常,而Pyramid正在生成视图,因此except IntegrityError:永远不会被激活。或者,更有可能的是,我完全错误地抓住了这个错误。

5 个答案:

答案 0 :(得分:21)

在金字塔中,如果您已配置会话(脚手架自动为您执行)以使用ZopeTransactionExtension,则在视图执行之前,会话不会被刷新/提交。如果要在视图中自己捕获任何SQL错误,则需要强制flush将SQL发送到引擎。 DBSession.flush()应在add(...)之后执行此操作。

<强>更新

我正在使用保存点的示例更新此答案,因为关于如何使用事务包执行此操作的示例非常少。

def create_unique_object(db, max_attempts=3):
    while True:
        sp = transaction.savepoint()
        try:
            obj = MyObject()
            obj.identifier = uuid.uuid4().hex
            db.add(obj)
            db.flush()
        except IntegrityError:
            sp.rollback()
            max_attempts -= 1
            if max_attempts < 1:
                raise
        else:
            return obj

obj = create_unique_object(DBSession)

请注意,如果不使用表级锁定,即使这很容易在事务之间重复,但它至少会显示如何使用保存点。

答案 1 :(得分:8)

您需要做的是捕获一般异常并输出其类;那么你可以使异常更具体。

except Exception as ex:
    print ex.__class__

答案 2 :(得分:4)

DBSession.commit()之前可能没有数据库操作,因此在IntegrityError的控制器代码已经返回后,在堆栈中稍后引发try/except

答案 3 :(得分:1)

编辑:使用回滚,上面编辑的答案是更好的方法。

-

如果你想在金字塔应用程序中间处理事务或者在序列结束时执行自动事务提交的事情,那么就没有必要发生的魔法。

如果先前的交易失败,请记住启动新交易

像这样:

def my_view(request):
   ... # Do things
   if success:
     try:
       instance = self._instance(**data)
       DBSession.add(instance)
       transaction.commit()
       return {'success': True}
     except IntegrityError as e:  # <--- Oh no! Duplicate unique key
       transaction.abort()
       transaction.begin() # <--- Start new transaction
       return {'success': False}

请注意,成功交易时调用.commit()很好,因此在成功调用后无需启动新事务。

如果事务处于失败状态,您只需要中止事务并启动新事务。

(如果事务不是这样的poc,你可以使用保存点并回滚到保存点而不是启动新事务;遗憾的是,这是不可能的,因为尝试提交会使已知的先前保存点无效。嗯?)(编辑:&lt; ---原来我错了......)

答案 4 :(得分:1)

我就是这样做的。

from contextlib import(
        contextmanager,
        )


@contextmanager
def session_scope():
    """Provide a transactional scope around a series of operations."""
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()



def create_user(email, firstname, lastname, password):

    new_user = Users(email, firstname, lastname, password)

    try:

        with session_scope() as session:

            session.add(new_user)

    except sqlalchemy.exc.IntegrityError as e:
        pass

http://docs.sqlalchemy.org/en/latest/orm/session_basics.html#when-do-i-construct-a-session-when-do-i-commit-it-and-when-do-i-close-it