IntegrityError valeur de clé en double viole la contrainte unique - django/postgres

je fais un suivi en ce qui concerne une question que j'ai demandé plus tôt dans laquelle j'ai cherché à chercher une conversion d'une requête mysql goofy/mal écrite en postgresql. Je crois que j'ai réussi. Bref, j'utilise des données qui ont été déplacées manuellement d'une base de données mysql vers une base de données postgres. J'utilise une requête qui ressemble à ceci:

  """
  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)
  )

j'ai des raisons de croire que cela fonctionne bien. Cependant, ceci a conduit à un nouveau problème. Lorsque en essayant de me soumettre, je reçois une erreur de django qui dit:

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

j'ai regardé plusieurs réponses posté ici et je n'ai pas trouvé la solution à mon problème (bien que les questions ont fait pour une lecture intéressante). Je le vois dans mes logs, ce qui est intéressant car je n'appelle jamais explicitement insert - django doit s'en charger:

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

Cependant, en essayant d'exécuter cela résulte dans l'erreur clé dupliquée. Le erreur réelle est jeté dans le code ci-dessous.

 # 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(

j'ai vérifié les séquences et ils semblent être dans l'ordre. À ce stade, Je ne suis pas sûr de savoir quoi faire - je suppose que c'est quelque chose du côté de django, mais je ne suis pas sûr. Tout commentaire serait très apprécié!

47
demandé sur Community 2012-06-18 23:39:46

8 réponses

cela m'est arrivé - il s'avère que vous devez resyncrer vos champs clés primaires dans Postgres. La clé est la déclaration SQL:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1)
106
répondu Hacking Life 2018-02-06 05:03:25

il semble qu'il y ait une différence de comportement connue entre les sauvegardes MySQL et SQLite (elles mettent à jour la prochaine clé primaire disponible même en insérant un objet avec un id explicite), et d'autres sauvegardes comme Postgres, Oracle, ... (ils ne sont pas).

il y a un billet décrivant la même question . Bien qu'il ait été fermé comme non valide, il fournit une indication qu'il y a une commande de gestion Django pour mettre à jour la prochaine clé disponible.

pour afficher le SQL mise à jour de tous les ID suivants pour l'application MyApp :

python manage.py sqlsequencereset MyApp

pour que la déclaration soit exécutée, vous pouvez la fournir comme entrée pour la commande de gestion dbshell . Pour bash, vous pouvez taper:

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

l'avantage des commandes de gestion est que les résumés loin de la base de données sous - jacente, de sorte qu'il fonctionnera même si plus tard la migration vers un autre serveur.

12
répondu Ad N 2016-10-17 14:24:40

en plus de zapphods réponse:

dans mon cas l'indexation était en effet incorrecte, puisque j'avais supprimé toutes les migrations, et la base de données probablement 10-15 fois lors du développement car je n'étais pas au stade de la migration de rien.

je recevais un IntegrityError sur finished_product_template_finishedproduct_pkey

Réindexer la table et redémarrez runserver:

j'utilisais pgadmin3 et quel que soit l'indice qui était incorrect et en jetant des erreurs clés en double, j'ai navigué sur le constraints et j'ai relancé.

enter image description here

et puis reindexe.

enter image description here

7
répondu jmunsch 2014-12-21 21:42:14

si vous avez copié manuellement les bases de données, il se peut que vous rencontriez le problème décrit ici .

3
répondu zaphod 2012-06-18 20:00:04

j'ai eu le même problème. J'avais une table existante dans mon application "inventaire" et je voulais ajouter de nouveaux disques dans django admin et j'ai eu ces messages:

la valeur de la clé dupliquée viole la contrainte unique" inventory_part_pkey" Détail: Key (part_id)=(1) existe déjà.

comme mentionné ci-dessus, exécutez le code ci-dessous pour obtenir générer une commande SQL pour réinitialiser l'id-s:

python manage.py sqlsequencereset inventory

dans mon cas python manage.py sqlsequencereset MyApp | python manage.py dbshell ne fonctionnait pas

  • donc j'ai copié la déclaration SQL générée.
  • ouvre alors pgAdmin pour postgreSQL et ouvre mon db.
  • cliqué sur le 6. icône (exécuter des requêtes SQL arbitraires)
  • copié la déclaration ce qui a été généré.

Dans mon cas, c'était:

BEGIN; SÉLECTIONNER setval(pg_get_serial_sequence('"inventory_signup"','id'), fusionnent(max("id"), 1), max("id") n'EST PAS nulle) à PARTIR de "inventory_signup"; SELECT setval(pg_get_serial_sequence('"inventory_supplier"','id'), fusionnent(max("id"), 1), max("id") n'EST PAS nulle) à PARTIR de "inventory_supplier"; COMMIT;

L'a exécuté avec F5.

cela a corrigé toutes mes tables et finalement ajouter de nouveaux enregistrements à la fin sans essayer de l'ajouter à id = 1 plus.

3
répondu Jozsef Turi 2017-09-22 15:45:50

la solution est que vous avez besoin de resync vos champs de clés primaires comme rapporté par" Hacking Life "qui a écrit un code SQL d'exemple mais, comme suggéré par" Ad N "est préférable d'exécuter la commande Django sqlsequencereset pour obtenir le code SQL exact que vous pouvez copier et passer ou exécuter avec une autre commande.

comme une autre amélioration à ces réponses, je vous suggérerais à vous et à d'autres lecteurs de ne pas ' copier et coller le code SQL mais, plus sûr, d'exécuter la requête SQL générée par sqlsequencereset depuis votre code python de cette façon ( en utilisant la base de données par défaut ):

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)

j'ai testé ce code avec Python3.6 , Django 2.0 et PostgreSQL 10 .

3
répondu Paolo Melchiorre 2018-05-12 11:34:21

j'ai rencontré cette erreur parce que je passais des arguments supplémentaires à la méthode de sauvegarde de la mauvaise manière.

pour ceux qui rencontrent ceci, essayez de forcer la mise à jour avec:

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

si vous obtenez une erreur que vous ne pouvez pas passer force_insert et force_update en même temps, vous passez probablement quelques arguments personnalisés de la mauvaise façon, comme je l'ai fait.

2
répondu jvannistelrooy 2014-01-02 13:01:14

si vous voulez réinitialiser le PK sur toutes vos tables, comme moi, vous pouvez utiliser le PostgreSQL voie recommandée :

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;

Après l'exécution de cette requête, vous devrez exécuter les résultats de la requête. En général, je copie et colle dans le bloc-notes. Puis je trouve et remplace "SELECT par SELECT et ;" par ; . Je copie et colle dans pgAdmin III et exécute la requête. Il réinitialise toutes les tables de la la base de données. Plus d'instructions "professionnelles" sont fournies au lien ci-dessus.

1
répondu Bobort 2018-03-14 17:56:30