Значение дублирующего ключа IntegrityError нарушает уникальное ограничение - django/postgres

Я продолжаю в отношении вопроса, который я задал ранее, в котором я пытался найти преобразование из глупого / плохо написанного запроса mysql в postgresql. Я считаю, что мне это удалось. В любом случае, я использую данные, которые были вручную перемещены из базы данных mysql в базу данных postgres. Я использую запрос, который выглядит так:

  """
  UPDATE krypdos_coderound cru

  set is_correct = case 
      when t.kv_values1 = t.kv_values2 then True 
      else False 
      end

  from 

  (select cr.id, 
    array_agg(
    case when kv1.code_round_id = cr.id 
    then kv1.option_id 
    else null end 
    ) as kv_values1,

    array_agg(
    case when kv2.code_round_id = cr_m.id 
    then kv2.option_id 
    else null end 
    ) as kv_values2

    from krypdos_coderound cr
     join krypdos_value kv1 on kv1.code_round_id = cr.id
     join krypdos_coderound cr_m 
       on cr_m.object_id=cr.object_id 
       and cr_m.content_type_id =cr.content_type_id 
     join krypdos_value kv2 on kv2.code_round_id = cr_m.id

   WHERE
     cr.is_master= False
     AND cr_m.is_master= True 
     AND cr.object_id=%s 
     AND cr.content_type_id=%s 

   GROUP BY cr.id  
  ) t

where t.id = cru.id
    """ % ( self.object_id, self.content_type.id)
  )

У меня есть основания полагать, что это работает хорошо. Однако это привело к новой проблеме. При попытке отправки я получаю сообщение об ошибке от django, в котором говорится:

IntegrityError at (some url): 
duplicate key value violates unique constraint "krypdos_value_pkey"

Я просмотрел несколько ответов, опубликованных здесь, и я не совсем нашел решение моей проблемы (хотя связанные вопросы сделали для некоторого интересного чтения). Я вижу это в моих журналах, что интересно, потому что я никогда не вызываю в явном виде insert- django должен с этим справиться:

   STATEMENT:  INSERT INTO "krypdos_value" ("code_round_id", "variable_id", "option_id", "confidence", "freetext")
   VALUES (1105935, 11, 55, NULL, E'') 
   RETURNING "krypdos_value"."id"

Однако, попытка выполнить это приводит к ошибке дубликата ключа. Фактическая ошибка брошена в коде ниже.

 # Delete current coding         CodeRound.objects.filter(object_id=o.id,content_type=object_type,is_master=True).delete()
  code_round = CodeRound(object_id=o.id,content_type=object_type,coded_by=request.user,comments=request.POST.get('_comments',None),is_master=True)
  code_round.save()
  for key in request.POST.keys():
    if key[0] != '_' or key != 'csrfmiddlewaretoken':
      options = request.POST.getlist(key)
      for option in options:
        Value(code_round=code_round,variable_id=key,option_id=option,confidence=request.POST.get('_confidence_'+key, None)).save()  #This is where it dies
  # Resave to set is_correct
  code_round.save()
  o.status = '3' 
  o.save(

Я проверил последовательности и тому подобное, и они, кажется, в порядке. На данный момент я не уверен, что делать - я предполагаю, что это что-то на конце Джанго, но я не уверен. Любая обратная связь будет высоко ценится!

14 ответов

Решение

Это случилось со мной - оказывается, вам нужно повторно синхронизировать поля первичного ключа в Postgres. Ключ - это оператор SQL:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1)

Кажется, это известное различие в поведении между MySQL и SQLite (они обновляют следующий доступный первичный ключ, даже когда вставляют объект с явным идентификатором), бэкэндами и другими бэкэндами, такими как Postgres, Oracle, ... (они этого не делают),

Есть билет, описывающий ту же проблему. Несмотря на то, что он был закрыт как недействительный, он дает подсказку, что есть команда управления Django для обновления следующего доступного ключа.

Чтобы отобразить SQL, обновляющий все следующие идентификаторы для приложения MyApp:

python manage.py sqlsequencereset MyApp

Чтобы выполнить инструкцию, вы можете предоставить ее в качестве входных данных для команды управления dbshell. Для bash вы можете набрать:

python manage.py sqlsequencereset MyApp | python manage.py dbshell

Преимущество команд управления заключается в том, что он абстрагирует базовый сервер БД, поэтому он будет работать даже при последующей миграции на другой сервер.

Я была такая же проблема. У меня была существующая таблица в моем приложении "инвентарь", и я хотел добавить новые записи в django admin, и я получил эти сообщения:

Повторяющееся значение ключа нарушает уникальное ограничение "inventory_part_pkey". ПОДРОБНЕЕ: Ключ (part_id) = (1) уже существует.

Как упоминалось ранее, запустите приведенный ниже код, чтобы получить команду SQL для сброса идентификаторов:

python manage.py sqlsequencereset inventory

В моем случае python manage.py sqlsequencereset MyApp | python manage.py dbshellне работал

  • Поэтому я скопировал сгенерированный оператор SQL.
  • Затем открыл pgAdmin для postgreSQL и открыл мой БД.
  • Нажмите на значок 6. (Выполнить произвольные запросы SQL)
  • Скопировал заявление, что было сгенерировано.

В моем случае это было:

НАЧАТЬ; ВЫБЕРИТЕ setval(pg_get_serial_sequence('"inventory_signup"','id'), coalesce(max("id"), 1), max("id") НЕ НУЖНО) FROM "inventory_signup"; ВЫБЕРИТЕ setval(pg_get_serial_sequence('"inventory_supplier"','id'), coalesce(max("id"), 1), max("id") не является нулевым) FROM "inventory_supplier"; COMMIT;

Выполнено это с F5.

Это исправило все мои таблицы и, наконец, добавило новые записи в конец, больше не пытаясь добавить его к id = 1.

В дополнение к ответным ответам:

В моем случае индексация была действительно неправильной, так как я удалил все миграции, и база данных, вероятно, 10-15 раз при разработке, поскольку я не находился в стадии миграции.

Я получаю IntegrityError на finished_product_template_finishedproduct_pkey

Переиндексировать таблицу и перезапустить runserver:

Я использовал pgadmin3 и для того, чтобы индекс был неправильным и выбрасывал дубликаты ключевых ошибок, я перешел к constraints и переиндексировать.

А потом переиндексировать.

Решение состоит в том, что вам нужно повторно синхронизировать поля первичного ключа, как сообщил "Hacking Life", который написал пример кода SQL, но, как это было предложено "Ad N", лучше запустить команду Django. sqlsequencereset чтобы получить точный код SQL, который вы можете скопировать и вставить или запустить с помощью другой команды.

В качестве дальнейшего улучшения этих ответов я бы предложил вам и другим читателям не копировать и вставлять код SQL, а, более безопасно, выполнять запрос SQL, сгенерированный sqlsequencereset из вашего кода Python таким образом (используя базу данных по умолчанию):

from django.core.management.color import no_style
from django.db import connection

from myapps.models import MyModel1, MyModel2


sequence_sql = connection.ops.sequence_reset_sql(no_style(), [MyModel1, MyModel2])
with connection.cursor() as cursor:
    for sql in sequence_sql:
        cursor.execute(sql)

Я тестировал этот код с Python3.6, Django 2.0 и PostgreSQL 10.

Если вы хотите сбросить PK во всех ваших таблицах, как и я, вы можете использовать PostgreSQL рекомендуемый способ:

SELECT 'SELECT SETVAL(' ||
       quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
       ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
       quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
FROM pg_class AS S,
     pg_depend AS D,
     pg_class AS T,
     pg_attribute AS C,
     pg_tables AS PGT
WHERE S.relkind = 'S'
    AND S.oid = D.objid
    AND D.refobjid = T.oid
    AND D.refobjid = C.attrelid
    AND D.refobjsubid = C.attnum
    AND T.relname = PGT.tablename
ORDER BY S.relname;

После выполнения этого запроса вам нужно будет выполнить результаты запроса. Я обычно копирую и вставляю в Блокнот. Потом нахожу и заменяю "SELECT с SELECT а также ;" с ;, Я копирую и вставляю в pgAdmin III и запускаю запрос. Сбрасывает все таблицы в базе данных. Более "профессиональные" инструкции приведены по ссылке выше.

Если вы скопировали базы данных вручную, вы можете столкнуться с проблемой, описанной здесь.

Я столкнулся с этой ошибкой, потому что неправильно передавал дополнительные аргументы методу сохранения.

Для любого, кто сталкивается с этим, попробуйте принудительно обновить с помощью:

instance_name.save(..., force_update=True)

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

Этот вопрос был задан около 9 лет назад, и многие люди предложили свои способы его решения.

Для меня я ввел свое пользовательское поле модели, но при создании суперпользователя я не просил emailбыть обязательным.

Теперь после создания суперпользователя мое поле электронной почты просто сохраняется как пустое или Null. Вот как я создал и сохранил нового пользователя

      obj = mymodel.objects.create_user(username='abc', password='abc')
obj.email = 'abc@abc.com'
obj.save()

Это просто выдало ошибку, говорящую duplicate-key-value-violatesв первой строке, потому что электронная почта по умолчанию была пустой, что было то же самое с пользователем-администратором. Джанго обнаружил дубликат!!!

Решение

  • Вариант 1: сделать электронную почту обязательной при создании любого пользователя (в том числе для суперпользователя)
  • Вариант 2: удалить unique=Trueи запустить миграцию
  • Вариант 3: если вы не знаете, где находятся дубликаты, вы либо удаляете столбец, либо можете очистить базу данных, используя python manage.py flush

Настоятельно рекомендуется узнать причину возникновения ошибки в вашем случае.

Вам просто нужно перейти в pgAdmin III и там выполнить свой скрипт с именем таблицы:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1);

Это правильное заявление. В основном это происходит, когда мы вставляем строки с полем id.

      SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename));

Основываясь на ответе Паоло Мельхиорре , я написал кусок как функцию, которая будет вызываться перед любым .save()

      from django.db import connection
def setSqlCursor(db_table):
    sql = """SELECT pg_catalog.setval(pg_get_serial_sequence('"""+db_table+"""', 'id'), MAX(id)) FROM """+db_table+""";"""
    with connection.cursor() as cursor:
        cursor.execute(sql)

У меня возникла аналогичная проблема, и, похоже, ничего не работало. Если вам нужны данные (т. Е. Нельзя исключить их при выполнении дампа), убедитесь, что вы отключили (прокомментировали) все приемники post_save. Я думаю, что данные будут импортированы, но из-за этого снова будет создана та же модель. Работал у меня.

Я получал ту же ошибку, что и ОП.

Я создал несколько моделей Django, создал таблицу Postgres на основе этих моделей и добавил несколько строк в таблицу Postgres через Django Admin. Затем я возился с некоторыми из столбцов в моделях (переходя на ForeignKeys и т. Д.), Но забыл перенести изменения.

Выполнение команд миграции решило мою проблему, что имеет смысл, учитывая приведенные выше ответы на SQL.

Чтобы увидеть, какие изменения будут применены, фактически не применяя их:
python manage.py makemigrations --dry-run --verbosity 3

Если вы довольны этими изменениями, запустите:
python manage.py makemigrations

Затем запустите:
python manage.py migrate

Другие вопросы по тегам