如何区分SQLAlchemy IntegrityError的原因?

DMJ*_*DMJ 8 sqlalchemy

IntegrityError当事务存在数据完整性问题时,SQLAlchemy 似乎只会抛出一般错误。当然,确切的查询和错误消息包含在异常中,这足以供人调试程序。然而,在为异常编写错误处理代码时,据我所知,似乎没有一个好的方法来检查哪个表上的哪个约束导致了错误。另外,异常是由该session.commit()行引发的,而不是由实际负责产生错误的行引发的,因此我也无法使用多个 try/ except 块来区分。

有没有一种方法,除了尝试以编程方式解析错误消息和/或查询之外,我可以将重复的主键错误与外键错误或失败的 CHECK 约束等区分开来?或者甚至只是一种判断哪个表的哪一列违反了数据完整性的方法?或者只是一种在导致错误的行上立即引发异常而不是等待事务提交的方法?

sna*_*erb 5

IntegrityError实例具有origstatement属性,可以通过检查这些属性来分别获取错误消息和失败的 SQL 语句。

给定这个模型:

class Foo(Base):
    __tablename__ = 'foo20201209'

    id = sa.Column(sa.Integer, primary_key=True)
    bar = sa.Column(sa.String(2), unique=True)
    baz = sa.Column(sa.Integer, sa.CheckConstraint('baz >= 0'), default=0)
Run Code Online (Sandbox Code Playgroud)

这段代码:

conn_strings = ['postgresql+psycopg2:///test',
                'mysql+mysqlconnector:///test',
                'sqlite://']


for cs in conn_strings:
    engine = sa.create_engine(cs)
    Base.metadata.drop_all(bind=engine)
    Base.metadata.create_all(bind=engine)

    session = orm.Session(bind=engine)

    for kwds in [{'bar': 'a'}, {'bar': 'a'}, {'bar': 'b', 'baz': -11}]:
        session.add(Foo(**kwds))
        try:
            session.commit()
        except sa.exc.IntegrityError as ex:
            print(ex.orig)
            print(ex.statement)
            print()
            session.rollback()
    session.close()
    engine.dispose()
Run Code Online (Sandbox Code Playgroud)

将产生以下输出:

duplicate key value violates unique constraint "foo20201209_bar_key"
DETAIL:  Key (bar)=(a) already exists.

INSERT INTO foo20201209 (bar, baz) VALUES (%(bar)s, %(baz)s) RETURNING foo20201209.id

new row for relation "foo20201209" violates check constraint "foo20201209_baz_check"
DETAIL:  Failing row contains (3, b, -11).

INSERT INTO foo20201209 (bar, baz) VALUES (%(bar)s, %(baz)s) RETURNING foo20201209.id

1062 (23000): Duplicate entry 'a' for key 'bar'
INSERT INTO foo20201209 (bar, baz) VALUES (%(bar)s, %(baz)s)

4025 (23000): CONSTRAINT `foo20201209.baz` failed for `test`.`foo20201209`
INSERT INTO foo20201209 (bar, baz) VALUES (%(bar)s, %(baz)s)

UNIQUE constraint failed: foo20201209.bar
INSERT INTO foo20201209 (bar, baz) VALUES (?, ?)

CHECK constraint failed: foo20201209
INSERT INTO foo20201209 (bar, baz) VALUES (?, ?)
Run Code Online (Sandbox Code Playgroud)


DMJ*_*DMJ 1

我最终使用了session.flush()更早的触发异常。我在有问题的行之前调用它一次(所以我100%确定异常不是由前面的行触发的),然后在 try/catch 块中再次调用它,以查看有问题的行是否导致一个错误。

我管理我对这个解决方案并不完全满意,但我还没有找到其他任何东西。我仍然很想听听是否有更好的解决方案,最好能准确地告诉我哪个表的哪个约束导致了错误。但是,这是一种可能对某人有所帮助的解决方法。