IntegrityError重复键值违反唯一约束 - django / postgres

时间:2012-06-18 19:39:46

标签: django postgresql duplicates unique-constraint database-integrity

我正在关注一个question that I asked earlier,我试图寻求从一个愚蠢/写得不好的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(

我检查了序列等等,它们似乎是有序的。在这一点上,我不知道该怎么做 - 我认为这是django的结果,但我不确定。任何反馈都将非常感谢!

13 个答案:

答案 0 :(得分:120)

这发生在我身上 - 事实证明你需要重新同步Postgres中的主键字段。关键是SQL语句:

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

答案 1 :(得分:17)

它似乎是MySQL和SQLite之间已知的行为差异(即使在插入具有显式id的对象时,它们也会更新下一个可用的主键)后端,以及其他后端,如Postgres,Oracle,......(它们不要)。

There is a ticket describing the same issue。即使它被关闭为无效,它也提示有一个Django管理命令来更新下一个可用密钥。

显示SQL更新应用程序的所有下一个ID MyApp

python manage.py sqlsequencereset MyApp

为了执行语句,您可以将其作为 dbshel​​l 管理命令的输入提供。对于bash,您可以输入:

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

管理命令的优点是抽象出底层数据库后端,因此即使稍后迁移到不同的后端也能正常工作。

答案 2 :(得分:7)

除了zapphods回答:

在我的情况下,索引确实是错误的,因为我已经删除了所有迁移,并且数据库在开发时可能需要10-15次,因为我还没有进行任何迁移。

我在finished_product_template_finishedproduct_pkey

上收到了IntegrityError

重新索引表并重新启动runserver:

我正在使用pgadmin3,对于哪个索引不正确并抛出重复键错误,我导航到constraints并重新编制索引。

enter image description here

然后重新编制索引。

enter image description here

答案 3 :(得分:7)

我有同样的问题。我的“库存”应用程序中有一个现有的表格,我想在django admin中添加新记录,我收到了以下消息:

  

重复键值违反了唯一约束“inventory_part_pkey”   DETAIL:Key(part_id)=(1)已经存在。

如前所述,运行代码以生成一个SQL命令来重置id-s:

python manage.py sqlsequencereset inventory

就我而言python manage.py sqlsequencereset MyApp | python manage.py dbshell 没有工作

  • 所以我复制了生成的SQL语句。
  • 然后打开pgAdmin for postgreSQL并打开我的数据库。
  • 单击6.图标(执行任意SQL查询)
  • 复制声明所生成的内容。

就我而言:

  

BEGIN;   SELECT setval(pg_get_serial_sequence('“inventory_signup”','id'),coalesce(max(“id”),1),max(“id”)IS not null)FROM“inventory_signup”;   SELECT setval(pg_get_serial_sequence('“inventory_supplier”','id'),coalesce(max(“id”),1),max(“id”)IS not null)FROM“inventory_supplier”;   COMMIT;

用F5执行。

这修复了我的所有表格,最后添加了新记录,最后不再尝试将其添加到id = 1。

答案 4 :(得分:3)

如果您手动复制了数据库,则可能会遇到issue described here

答案 5 :(得分:3)

解决方案是您需要重新同步主键字段,如#34; Hacking Life"谁编写了一个示例SQL代码,但正如" Ad N"最好运行Django命令sqlsequencereset以获取可以复制和过去或使用其他命令运行的确切SQL代码。

作为对这些答案的进一步改进,我建议你和其他读者不要'复制并粘贴SQL代码,但更安全的是,以这种方式从您的python代码中执行sqlsequencereset生成的SQL查询(使用默认数据库):

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 测试了此代码。

答案 6 :(得分:2)

我遇到了这个错误,因为我以错误的方式将额外的参数传递给save方法。

对于遇到此问题的任何人,请尝试强制UPDATE:

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

如果您收到的错误是无法同时传递force_insertforce_update,那么您可能会以错误的方式传递一些自定义参数,就像我一样。

答案 7 :(得分:2)

如果你想重置所有牌桌上的PK,就像我一样,你可以使用PostgreSQL recommended way

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并运行查询。它会重置数据库中的所有表。更多"专业"说明在上面的链接中提供。

答案 8 :(得分:1)

您只需要转到pgAdmin III,然后使用表名执行脚本:

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

答案 9 :(得分:0)

我遇到了与OP相同的错误。

我创建了一些Django模型,基于这些模型创建了Postgres表,并通过Django Admin将一些行添加到Postgres表中。然后,我摆弄了模型中的某些列(更改ForeignKeys等),却忘记了迁移更改。

运行迁移命令解决了我的问题,鉴于上面的SQL回答,这很有意义。

查看将要应用的更改,而无需实际应用它们:
python manage.py makemigrations --dry-run --verbosity 3

如果您对这些更改感到满意,请运行:
python manage.py makemigrations

然后运行:
python manage.py migrate

答案 10 :(得分:0)

我遇到了类似的问题,但似乎没有任何效果。如果您需要数据(即在执行转储时不能排除数据),请确保已关闭(注释)了所有post_save接收器。我认为数据将被导入,但是由于这些原因,它将再次创建相同的模型。为我工作。

答案 11 :(得分:0)

基于Paolo Melchiorre's answer,我写了一个块作为在任何 .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)

答案 12 :(得分:0)

这是正确的说法。大多数情况下,当我们插入带有 id 字段的行时会发生这种情况。

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