IntegrityError: различать уникальное ограничение и недействительные нарушения

У меня есть этот код:

try: principal = cls.objects.create( user_id=user.id, email=user.email, path='something' ) except IntegrityError: principal = cls.objects.get( user_id=user.id, email=user.email ) 

Он пытается создать пользователя с данным идентификатором и электронной почтой, а если он уже существует, он пытается получить существующую запись.

Я знаю, что это плохая конструкция, и в любом случае она будет реорганизована. Но мой вопрос таков:

Как определить, какой тип IntegrityError произошел: тот, который связан с unique нарушением ограничения (есть уникальный ключ (user_id, email)) или тот, который связан с not null ограничением ( path не может быть пустым)?

3 Solutions collect form web for “IntegrityError: различать уникальное ограничение и недействительные нарушения”

psycopg2 предоставляет SQLSTATE за исключением члена pgcode , который дает вам достаточно мелкую информацию об ошибках для соответствия.

 python3 >>> import psycopg2 >>> conn = psycopg2.connect("dbname=regress") >>> curs = conn.cursor() >>> try: ... curs.execute("INVALID;") ... except Exception as ex: ... xx = ex >>> xx.pgcode '42601' 

См. Приложение A: Коды ошибок в руководстве PostgreSQL для значений кода. Обратите внимание, что вы можете сопоставить грубо на первых двух символах для широких категорий. В этом случае я вижу, что SQLSTATE 42601 является syntax_error в категории « Syntax Error or Access Rule Violation .

Коды, которые вы хотите:

 23505 unique_violation 23502 not_null_violation 

так что вы могли бы написать:

 try: principal = cls.objects.create( user_id=user.id, email=user.email, path='something' ) except IntegrityError as ex: if ex.pgcode == '23505': principal = cls.objects.get( user_id=user.id, email=user.email ) else: raise - try: principal = cls.objects.create( user_id=user.id, email=user.email, path='something' ) except IntegrityError as ex: if ex.pgcode == '23505': principal = cls.objects.get( user_id=user.id, email=user.email ) else: raise 

Тем не менее, это плохой способ сделать upsert или merge . @ pr0gg3d, по-видимому, прав, предлагая правильный способ сделать это с Django; Я не делаю Django, поэтому я не могу прокомментировать этот бит. Для получения общей информации о upsert / merge см. Статью depesz по этой теме .

Лучше использовать:

 try: obj, created = cls.objects.get_or_create(user_id=user.id, email=user.email) except IntegrityError: .... 

как в https://docs.djangoproject.com/en/dev/ref/models/querysets/#get-or-create

IntegrityError должен быть поднят только в том случае, если есть нарушение ограничения NOT NULL . Кроме того, вы можете использовать created флаг, чтобы узнать, существует ли объект.

Дополнение от 9-6-2017:

Довольно элегантный способ сделать это – try / exc.__cause__ except IntegrityError as exc , а затем использовать некоторые полезные атрибуты для exc.__cause__ и exc.__cause__.diag (диагностический класс, который дает вам некоторую другую сверхсовременную информацию об ошибке – вы можете исследовать его самостоятельно с помощью dir(exc.__cause__.diag) ).

Первый, который вы можете использовать, был описан выше. Чтобы сделать ваш код более перспективным, вы можете напрямую ссылаться на коды psycopg2, и вы даже можете проверить ограничение, которое было нарушено, с использованием класса диагностики, о котором я упоминал выше:

 except IntegrityError as exc: from psycopg2 import errorcodes as pg_errorcodes assert exc.__cause__.pgcode == pg_errorcodes.UNIQUE_VIOLATION assert exc.__cause__.diag.constraint_name == 'tablename_colA_colB_unique_constraint' 

редактирование для уточнения: я должен использовать __cause__ accessor, потому что я использую Django, поэтому, чтобы перейти в класс psycopg2 IntegrityError, я должен вызвать exc.__cause__

  • psycopg2: как выполнить вакуумный запрос postgresql в скрипте python
  • Как указать параметр psycopg2 для массива для временных меток (datetimes)
  • psycopg2 "IndexError: tuple index out the range" Ошибка при использовании оператора "%" с аргументами tuple
  • построить динамический запрос SQL с помощью библиотеки python psycopg2 и использовать хорошие инструменты для преобразования типов
  • Доступ к базе данных из рабочего процесса
  • Открытие соединения postgres в psycopg2 приводит к сбою python
  • Libssl и libcrypto, вызывающие dyld: библиотека не загружена: /usr/lib/libpq.5.dylib
  • Изображение Psycopg2 не найдено
  • Python - лучший язык программирования в мире.