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é!
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)
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.
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é.
et puis reindexe.
si vous avez copié manuellement les bases de données, il se peut que vous rencontriez le problème décrit ici .
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.
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 .
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.
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.