web-dev-qa-db-fra.com

ne peut pas intercepter SQLAlchemy IntegrityError

Malgré tous mes efforts, je n'arrive pas à détecter correctement l'intégrale 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

Comme à quoi on pourrait s'attendre:

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

J'ai essayé de explicitement:

from sqlalchemy.exc import IntegrityError

METTRE À JOUR:

J'ai trouvé quelque chose qui semble correspondre à ce qui se passe ici, où l'erreur d'intégrité n'est pas générée tant que la session n'est pas vidée de la base de données, et après l'exécution des try/exceptblocks: Essayer d'attraper une erreur d'intégrité avec SQLAlchemy

Cependant, l'ajout de session.flush() dans le bloc try donne la valeur 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)
20
Chrispy

Dès que la IntegrityError est générée, que vous ayez détecté l'erreur ou non, la session dans laquelle vous étiez en train de travailler est invalidée. Comme le deuxième message d'erreur vous indique, To begin a new transaction with this Session, first issue Session.rollback()., de continuer à utiliser la session, vous devez émettre une session.rollback().

Je ne peux pas dire avec certitude, mais je suppose que vous ou votre infrastructure Web essayez de continuer à utiliser la session qui a généré l’intégrité IntegrityError. Je vous recommande d’émettre une session.rollback() après avoir relevé l’exception ou dans votre fonction handle_elegantly

Si vous courez ci-dessous, vous verrez ce que je veux dire:

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):
    __table= '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 __== '__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")
19
Jason

J'ai le même besoin dans mon application Flask, je le gère comme ci-dessous et cela fonctionne:

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()
16
JsonBruce

SQLALCHEMY_COMMIT_ON_TEARDOWN = False

0
ivan