web-dev-qa-db-fra.com

La valeur de clé en double IntegrityError viole une contrainte unique - Django/postgres

Je poursuis en ce qui concerne une question que j'ai posée précédemment dans laquelle je cherchais à obtenir une conversion d'une requête mysql maladroite/mal écrite en postgresql. Je crois avoir réussi avec ça. Quoi qu'il en soit, j'utilise des données 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, cela a conduit à un nouveau problème. En essayant de soumettre, Django affiche une erreur qui dit:

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

J'ai examiné plusieurs des réponses publiées ici et je n'ai pas encore trouvé la solution à mon problème (bien que les questions connexes aient fourni une lecture intéressante). Je le vois dans mes journaux, ce qui est intéressant car je n’appelle jamais explicitement insert-Django doit le gérer:

   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, si vous essayez de l'exécuter, cela entraîne une erreur de clé en double. L'erreur réelle est renvoyée 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 autres et elles semblent être en ordre. À ce stade, je ne sais pas quoi faire - je suppose que c'est quelque chose du côté de Django mais je ne suis pas sûr. Tout retour serait apprécié!

49
the_man_slim

Cela m'est arrivé - il s'avère que vous devez resynchroniser vos champs de clé primaire dans Postgres. La clé est l'instruction SQL: 

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

Il semble que ce soit une différence de comportement connue entre MySQL et SQLite (ils mettent à jour la prochaine clé primaire disponible même lors de l'insertion d'un objet avec un identifiant explicite) .

Il existe un ticket décrivant le même problème . Même s'il a été fermé pour invalide, il indique qu'il existe une commande de gestion Django pour mettre à jour la clé disponible suivante.

Pour afficher le code SQL mettant à jour tous les identifiants suivants pour l'application MyApp:

python manage.py sqlsequencereset MyApp

Pour que l'instruction 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 réside dans l'abstraction du backend de base de données sous-jacent. Ainsi, cela fonctionnera même si vous migrez ultérieurement vers un backend différent.

16
Ad N

En plus de zapphods, répondez:

Dans mon cas, l'indexation était en effet incorrecte, car j'avais supprimé toutes les migrations, et la base de données était probablement 10 à 15 fois sous développement, car je n'étais pas encore en train de migrer.

J'obtenais un IntegrityError sur finished_product_template_finishedproduct_pkey 

Réindexez la table et redémarrez le serveur d'exécution:

J'utilisais pgadmin3 et quel que soit l'index qui était incorrect et jettais les erreurs de clé en double, j'ai navigué dans la variable constraints et réindexé.

enter image description here

Et puis réindexé.

enter image description here

7
jmunsch

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

La valeur de clé en double viole la contrainte unique "inventory_part_pkey" DETAIL: La clé (part_id) = (1) existe déjà.

Comme mentionné précédemment, lancez le code ci-dessous pour obtenir une commande SQL permettant de 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

  • J'ai donc copié l'instruction SQL générée.
  • Ensuite, ouvrez pgAdmin pour postgreSQL et ouvrez ma base de données.
  • Cliqué sur l'icône 6. (Exécuter des requêtes SQL arbitraires)
  • Copié la déclaration de ce qui a été généré.

Dans mon cas c'était:

COMMENCER; SELECT setval (pg_get_serial_sequence ('"inventaire_signup"', "id"), coalesce (max ("id"), 1), max ("id") IS NON null) FROM "inventaire_signup"; SELECT setval (pg_get_serial_sequence ('"inventaire_supplier"', "id"), coalesce (max ("id"), 1), max ("id") IS NON null) FROM "inventaire_supplier"; COMMETTRE;

Exécuté avec F5.

Cela a corrigé toutes mes tables et finalement ajouté de nouveaux enregistrements à la fin en essayant de ne plus l'ajouter à id = 1.

6
Jozsef Turi

La solution est que vous devez resynchroniser vos champs de clé primaire comme indiqué par "Hacking Life" qui a écrit un exemple de code SQL mais, comme suggéré par "Ad N", il est préférable d'exécuter la commande Django sqlsequencereset peut copier et coller ou exécuter avec une autre commande.

Pour améliorer encore ces réponses, je vous recommanderais, ainsi qu’à un autre lecteur, de ne pas copier et coller le code SQL mais, plus sûrement, d’exécuter la requête SQL générée par sqlsequencereset depuis votre code python de cette manière (using 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
Paolo Melchiorre

Si vous avez copié manuellement les bases de données, vous risquez de rencontrer le problème décrit ici .

3
zaphod

J'ai rencontré cette erreur parce que je transmettais des arguments supplémentaires à la méthode de sauvegarde de manière incorrecte. 

Pour tous ceux qui rencontrent cela, essayez de forcer UPDATE avec:

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

Si vous obtenez une erreur indiquant que vous ne pouvez pas transmettre force_insert et force_update en même temps, vous transmettez probablement des arguments personnalisés de manière incorrecte, comme je l'ai fait.

2
jvannistelrooy

Si vous voulez réinitialiser le PK sur toutes vos tables, comme moi, vous pouvez utiliser le méthode recommandée par 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;

Après avoir exécuté cette requête, vous devrez exécuter les résultats de la requête. Je copie et colle généralement dans le Bloc-notes. Ensuite, je trouve et remplace "SELECT par SELECT et ;" par ;. Je copie et colle dans pgAdmin III et lance la requête. Il réinitialise toutes les tables de la base de données. Des instructions plus "professionnelles" sont fournies sur le lien ci-dessus.

1
Bobort

J'avais la même erreur que l'OP.

J'avais créé des modèles Django, créé une table Postgres basée sur les modèles et ajouté quelques lignes à la table Postgres via Django Admin. Ensuite, j'ai manipulé certaines des colonnes des modèles (modification de ForeignKeys, etc.), mais j'avais oublié de migrer les modifications.

L'exécution des commandes de migration a résolu mon problème, ce qui est logique compte tenu des réponses SQL ci-dessus.

Pour voir quels changements seraient appliqués, sans les appliquer réellement:
python manage.py makemigrations --dry-run --verbosity 3

Si vous êtes satisfait de ces changements, lancez:
python manage.py makemigrations

Puis lancez:
python manage.py migrate

0
allardbrain