Ошибка, которую вы опубликовали в своем вопросе, не является ошибкой, которая возникла. Полное сообщение об ошибке:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "model_name_key"
Ключевой частью является ошибка SQLAlchemy, которую вы по какой-то причине решили пропустить. SQLAlchemy улавливает исходную ошибку, оборачивает ее в свою собственную ошибку и выдает ее.
но в Python это гораздо более запутанно ... Почему Python скрывает это?
Это не запутывание, ничего не скрыто, поведение задокументировано, специфично для используемых вами фреймворков и не поддерживается языком Python. SQLAlchemy - это библиотека абстракций, и если бы она вызвала исключения, специфичные для базового адаптера dpapi, это значительно уменьшило бы переносимость кода, написанного внутри него.
Из документов :
SQLAlchemy не генерирует эти исключения напрямую. Вместо этого они перехватываются из драйвера базы данных и переносятся исключением DBAIError, предоставляемым SQLAlchemy, однако обмен сообщениями в исключении генерируется драйвером, а не SQLAlchemy.
Исключения, создаваемые уровнем dbapi,завернутый в подкласс sqlalchemy.exc.DBAPIError , где отмечается:
Обернутый объект исключения доступен в атрибуте orig
.
Так что очень просто поймать исключение SQLAlchemy и проверить исходное исключение, которое является экземпляром psycopg2.errors.UniqueViolation
, как и следовало ожидать. Однако, если ваша обработка ошибок не очень специфична для типа, вызванного слоем dbapi, я бы предположил, что проверка базового типа может быть ненужной, поскольку возникшее исключение SQLAlchemy предоставит достаточно информации времени выполнения, чтобы выполнить то, что вам нужно сделать.
Вот пример сценария, который вызывает sqlalchemy.exc.IntegrityError
, перехватывает его, проверяет основное исключение с помощью атрибута orig
и вызывает альтернативное локально определяемое исключение.
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from psycopg2.errors import UniqueViolation
engine = create_engine("postgresql+psycopg2://some-user:mysecretpassword@localhost:5432/some-user")
Base = declarative_base()
Session = sessionmaker(bind=engine)
class BadRequest(Exception):
pass
class Model(Base):
__tablename__ = "model"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
if __name__ == "__main__":
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
s = Session()
s.add(Model(name="a"))
s.commit()
s.add(Model(name="a"))
try:
s.commit()
except IntegrityError as e:
assert isinstance(e.orig, UniqueViolation) # proves the original exception
raise BadRequest from e
И это поднимает:
sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "model_name_key"
DETAIL: Key (name)=(a) already exists.
[SQL: INSERT INTO model (name) VALUES (%(name)s) RETURNING model.id]
[parameters: {'name': 'a'}]
(Background on this error at: http://sqlalche.me/e/gkpj)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File ".\main.py", line 36, in <module>
raise BadRequest from e
__main__.BadRequest