Python 无法捕获 SQLAlchemy IntegrityError

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24522290/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 04:44:30  来源:igfitidea点击:

cannot catch SQLAlchemy IntegrityError

pythonexceptionexception-handlingerror-handlingsqlalchemy

提问by Chrispy

Try as I might, I can't seem to catch the sqlalchemy IntegrityError correctly:

尽我所能,我似乎无法正确捕获 sqlalchemy IntegrityError:

from sqlalchemy import exc

try:
    insert_record()
except exc.IntegrityError, exc:
    print exc # this is never called
    handle_elegantly() # this is never called

As what one might expect:

正如人们所期望的那样:

IntegrityError: (IntegrityError) insert or update on table "my_table" 
                violates foreign key constraint "my_table_some_column_fkey"

I've tried to explicitly:

我试图明确:

from sqlalchemy.exc import IntegrityError

UPDATE:

更新:

I found something that seems to fit what's happening here, where Integrity Error isn't thrown until the session is flushed to the db, and after the try/exceptblocks have been executed: Trying to catch integrity error with SQLAlchemy

我发现了一些似乎适合这里发生的事情的东西,在会话刷新到数据库之前不会抛出完整性错误,并且在try/except块被执行之后:尝试使用 SQLAlchemy 捕获完整性错误

However, adding session.flush()in the tryblock yields an InvalidRequestError:

但是,session.flush()try块中添加会产生一个InvalidRequestError

ERROR:root:This Session's transaction has been rolled back due to a previous 
           exception during flush. To begin a new transaction with this Session, 
           first issue Session.rollback(). 
           Original exception was: (IntegrityError)

回答by Jason

As soon as the IntegrityErroris raised, regardless of whether or not you've caught the error, the session you were working in is invalidated. As the second error message is instructing you, To begin a new transaction with this Session, first issue Session.rollback()., to continue using the session you'll need to issue a session.rollback()

一旦IntegrityError引发,无论您是否发现错误,您正在使用的会话都将失效。由于第二条错误消息指示您To begin a new transaction with this Session, first issue Session.rollback().继续使用会话,您需要发出session.rollback()

I cannot say for sure, but I am guessing you or your web framework is attempting to continue using the session which raised the IntegrityError in some way. I recommend you issue a session.rollback()either after you catch the exception or in your handle_elegantlyfunction.

我不能肯定地说,但我猜您或您的 Web 框架正试图继续使用以某种方式引发 IntegrityError 的会话。我建议您session.rollback()在捕获异常后或在您的handle_elegantly函数中发出 a 。

If you run the below you'll see what I mean:

如果你运行下面的你会明白我的意思:

from sqlalchemy import types
from sqlalchemy import exc
from sqlalchemy import create_engine
from sqlalchemy.schema import Column
from zope.sqlalchemy import ZopeTransactionExtension
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session

Base = declarative_base()


class User(Base):
    __tablename__ = 'user'
    name = Column(types.String, primary_key=True)


def handle_elegantly(name):
    session = DBSession()
    session.add(User(name=name))
    session.flush()
    print 'Exception elegantly handled!!\n'


def pretend_view(request):
    """Pretend view in a Pyramid application using pyramid_tm"""
    session = DBSession()
    user = User()
    print '\n-------Here we rollback before continuing -------'
    try:
        session.add(user)
        session.flush()
    except exc.IntegrityError:
        session.rollback()
        handle_elegantly('This will run fine')

    print '\n------- Here we do not, and this will error -------'
    try:
        session.add(user)
        session.flush()
    except exc.IntegrityError:
        handle_elegantly('Exception will be raised')


if __name__ == '__main__':
    engine = create_engine('sqlite://')
    global DBSession
    DBSession = scoped_session(
        sessionmaker(extension=ZopeTransactionExtension()))
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    Base.metadata.create_all()
    pretend_view("dummy request")

回答by JsonBruce

I have the same need in my Flask application, I handle it like below and it works:

我在 Flask 应用程序中也有同样的需求,我像下面那样处理它并且它有效:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import exc

db = SQLAlchemy(Flask(__name__))

try:
     db.session.add(resource)
     return db.session.commit()
except exc.IntegrityError:
     db.session.rollback()

回答by ivan

SQLALCHEMY_COMMIT_ON_TEARDOWN = False