如下DMJ所评论的我的答案,不同的数据库引擎在完整性错误发生时会发出不同的错误消息,而SQLAlchemy并不尝试以一致的方式呈现这些消息,因此问题的简短答案是:您无法从异常中获得有用信息,除非解析错误消息。
那么,如果约束以一致的方式命名并且这些名称出现在错误消息中,则解析错误消息可能不是那么困难。在这方面,SQLAlchemy提供了一些帮助:我们可以创建一个
元数据对象,并定义一个约束命名规则(
文档)。有了这个约定,我们创建的任何约束都将遵循它,我们可以匹配错误消息中的约束名称,并使用这些匹配项在元数据中查找表和约束对象。
以下是使用这些约定解析错误消息的示例。我没有涵盖所有可能的约束类型,也没有处理Sqlite的唯一键冲突消息,这些留给读者作为练习;-)
import re
import sqlalchemy as sa
convention = {
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
}
metadata = sa.MetaData(naming_convention=convention)
tbl = sa.Table(
't65189213',
metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('foo', sa.String(2), unique=True),
sa.Column('bar', sa.Integer, default=0),
sa.CheckConstraint('bar >= 0', name='positive_bar'),
)
pattern = r'(?P<type_key>uq|ck)_(?P<table>[a-z][a-z0-9]+)_[a-z_]+'
regex = re.compile(pattern)
def parse_exception(ex):
types = {'ck': 'Check constraint', 'uq': 'Unique key'}
m = regex.search(str(ex.orig))
if m:
type_ = types[m.groupdict()['type_key']]
table_name = m.groupdict()['table']
constraint_name = m.group(0)
table = metadata.tables[table_name]
constraint = next(c for c in table.constraints if c.name == constraint_name)
columns = ','.join(constraint.columns.keys())
return f'{type_} {constraint_name} has been violated on table {table_name!r} columns: {columns}'
return f'{ex.orig}'
conn_strings = ['postgresql+psycopg2:///test',
'mysql+mysqlconnector://root:root@localhost/test',
'sqlite://']
for cs in conn_strings:
engine = sa.create_engine(cs, future=True)
tbl.drop(engine, checkfirst=True)
tbl.create(engine)
with engine.connect() as conn:
print(engine.dialect.name)
for kwds in [{'foo': 'a'}, {'foo': 'a'}, {'foo': 'b', 'bar': -11}]:
try:
conn.execute(tbl.insert(), kwds)
conn.commit()
except sa.exc.IntegrityError as ex:
print(parse_exception(ex))
conn.rollback()
print('-'*10)
engine.dispose()
输出:
postgresql
Unique key uq_t65189213_foo has been violated on table 't65189213' columns: foo
Check constraint ck_t65189213_positive_bar has been violated on table 't65189213' columns:
----------
mysql
Unique key uq_t65189213_foo has been violated on table 't65189213' columns: foo
Check constraint ck_t65189213_positive_bar has been violated on table 't65189213' columns:
----------
sqlite
UNIQUE constraint failed: t65189213.foo
Check constraint ck_t65189213_positive_bar has been violated on table 't65189213' columns:
----------
Foo
的引用,对Foo.bar
的引用以及一个常量或枚举值,指示它是 CHECK、UNIQUE 还是任何其他类型的约束。看起来 SQLAlchemy 没有这种能力... - DMJ