Не удается поймать SQLAlchemy IntegrityError

Попытайтесь, как я мог, я не могу правильно поймать 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

Как и следовало ожидать:

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

Я попытался явно:

from sqlalchemy.exc import IntegrityError

UPDATE:

Я нашел что-то похожее на то, что происходит здесь, где Integrity Error не выбрасывается до тех пор, пока сеанс не будет сброшен на db, а после выполнения блоков try/except: Попытка поймать ошибку целостности с помощью SQLAlchemy

Однако добавление session.flush() в блок try дает InvalidRequestError:

ERROR:root:This Session 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)

Ответы

Ответ 1

У меня такая же потребность в моем приложении 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 as e:
     db.session().rollback()

Ответ 2

Как только IntegrityError будет поднят, независимо от того, поймала ли вы ошибку, сеанс, в котором вы работали, недействителен. Поскольку второе сообщение об ошибке инструктирует вас, To begin a new transaction with this Session, first issue Session.rollback()., чтобы продолжить использование сеанса, вам нужно выпустить session.rollback()

Я не могу сказать точно, но я предполагаю, что вы или ваша веб-инфраструктура пытаетесь продолжить использование сеанса, который каким-то образом поднял IntegrityError. Я рекомендую вам выпустить session.rollback() либо после того, как вы поймаете исключение, либо в своей функции handle_elegantly.

Если вы запустите ниже, вы увидите, что я имею в виду:

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")

Ответ 3

SQLALCHEMY_COMMIT_ON_TEARDOWN = False