Der doppelte Schlüsselwert von IntegrityError verletzt die eindeutige Einschränkung - django / postgres

70

Ich verfolge eine Frage, die ich zuvor gestellt habe und in der ich versucht habe, eine Konvertierung von einer doofen / schlecht geschriebenen MySQL-Abfrage zu Postgresql anzustreben. Ich glaube, das ist mir gelungen. Wie auch immer, ich verwende Daten, die manuell von einer MySQL-Datenbank in eine Postgres-Datenbank verschoben wurden. Ich verwende eine Abfrage, die so aussieht:

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

Ich habe Grund zu der Annahme, dass dies gut funktioniert. Dies hat jedoch zu einem neuen Problem geführt. Beim Versuch, etwas einzureichen, erhalte ich von django eine Fehlermeldung, die besagt:

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

Ich habe mir einige der hier veröffentlichten Antworten angesehen und die Lösung für mein Problem nicht ganz gefunden (obwohl die damit verbundenen Fragen für eine interessante Lektüre gesorgt haben). Ich sehe dies in meinen Protokollen, was interessant ist, weil ich insert nicht explizit aufrufe. Django muss damit umgehen:

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

Der Versuch, dies auszuführen, führt jedoch zu einem doppelten Schlüsselfehler. Der eigentliche Fehler wird im folgenden Code ausgegeben.

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

Ich habe die Sequenzen und dergleichen überprüft und sie scheinen in Ordnung zu sein. An diesem Punkt bin ich mir nicht sicher, was ich tun soll - ich nehme an, es ist etwas am Ende von Django, aber ich bin mir nicht sicher. Jedes Feedback wäre sehr dankbar!

the_man_slim
quelle
1
Nebenbei: Nach den Gesetzen von De Morgan ist Ihr Zustand key[0] != '_' or key != 'csrfmiddlewaretoken'gleichbedeutend mit not (key[0] == '_' and key == 'csrfmiddlewaretoken'). Es sollte leicht zu erkennen sein, dass die innere Bedingung niemals erfüllt ist, also äquivalent zu not (False)oder mit anderen Worten True. Aber warum sollte man sich dann mit dem beschäftigen if?
Jonas Kölker
python manage.py sqlsequencereset <app> | python manage.py dbshell
Benutzer
Diese vorherige Antwort gibt mehr Details und Licht auf das Thema: stackoverflow.com/questions/244243/…
RedSands

Antworten:

157

Das ist mir passiert - es stellt sich heraus, dass Sie Ihre Primärschlüsselfelder in Postgres neu synchronisieren müssen. Der Schlüssel ist die SQL-Anweisung:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1);
Das Leben hacken
quelle
Das war's! Ich dachte, ich hätte dieses Problem behoben, aber es stellte sich heraus, dass ich den falschen Wert zurückgesetzt habe.
the_man_slim
1
Ich kenne eine Menge Leute, die dieses Problem hatten - ich bin froh, dass Sie es gelöst haben!
Hacking Life
Ich wollte @HackingLife fragen, ob Sie mehr darüber wissen, warum dies passiert ... es stellte sich für uns heraus, dass wir alle Daten aus einer anderen Datenbank synchronisierten, indem wir sie direkt kopierten (was in seiner Antwort zaphod erwähnt wurde). . Als wir damit aufhörten und die Hauptdatenbank direkt verwendeten, war die Primärschlüsselfolge beim Hinzufügen eines neuen Modells nicht inkrementiert worden, was zu diesem Fehler führte.
AJP
Ich hatte das gleiche Problem, nachdem ich Inhalte manuell über ein SQL-Skript importiert hatte, und es gab Probleme, als ich zu meiner Benutzeroberfläche in Flask zurückkehrte, um ein neues Element hinzuzufügen. Das Aktualisieren der Primärschlüsselfeldsequenz hat den Trick für mich getan. Danke für die Fehlerbehebung!
Joshua Powell
Wow .. Ich habe Heroku verwendet und musste die Datenbank auf Amazon AWS RDS verschieben. Ich habe bei aws exportiert und in eine frische Postgres-Datenbank importiert, und dieses Problem ist aufgetreten. Hat die ID-Sequenz wie oben erwähnt auf alle Tabellen zurückgesetzt und es hat wie ein Zauber funktioniert. Vielen Dank!
pavanw3b
29

Es scheint ein bekannter Unterschied im Verhalten zwischen MySQL und SQLite (sie aktualisieren den nächsten verfügbaren Primärschlüssel, selbst wenn ein Objekt mit einer expliziten ID eingefügt wird) und anderen Backends wie Postgres, Oracle, ... zu sein (dies ist nicht der Fall). .

Es gibt ein Ticket, das das gleiche Problem beschreibt . Obwohl es als ungültig geschlossen wurde, gibt es einen Hinweis darauf, dass ein Django-Verwaltungsbefehl zum Aktualisieren des nächsten verfügbaren Schlüssels vorhanden ist.

So zeigen Sie die SQL-Aktualisierung aller nächsten IDs für die Anwendung MyApp an :

python manage.py sqlsequencereset MyApp

Damit die Anweisung ausgeführt werden kann, können Sie sie als Eingabe für den Befehl dbshell management bereitstellen . Für Bash können Sie Folgendes eingeben:

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

Der Vorteil der Verwaltungsbefehle besteht darin, dass das zugrunde liegende DB-Backend abstrahiert wird, sodass es auch bei einer späteren Migration auf ein anderes Backend funktioniert.

Anzeige N.
quelle
1
Dies rettete meinen Tag
Keval
24

Ich hatte eine vorhandene Tabelle in meiner "Inventar" -App und wollte neue Datensätze in Django admin hinzufügen. Dabei wurde folgende Fehlermeldung angezeigt:

Doppelter Schlüsselwert verletzt eindeutige Einschränkung "inventar_part_pkey" DETAIL: Schlüssel (part_id) = (1) existiert bereits.

Wie bereits erwähnt, führe ich den folgenden Code aus, um den SQL-Befehl zum Zurücksetzen der IDs zu erhalten:

python manage.py sqlsequencereset inventory

Verrohrung der python manage.py sqlsequencereset inventory | python manage.py dbshell zur Schale funktionierte nicht

  • Also habe ich den generierten Raw-SQL-Befehl kopiert
  • Dann öffnete pgAdmin3 https://www.pgadmin.org für postgreSQL und öffnete meine Datenbank
  • Klicken Sie auf das 6. Symbol (Beliebige SQL-Abfragen ausführen)
  • Kopierte die Anweisung, was generiert wurde

In meinem Fall war der Raw-SQL-Befehl:

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;

Habe es mit F5 ausgeführt.

Das hat alles repariert.

jturi
quelle
3
Diese Antwort hat mir gerade den Tag gerettet!
Gambit2007
2
Diese Antwort sollte die sein, die Sie tatsächlich brauchen - es ist der richtige Weg dafür!
Dmitry Arkhipenko
Sie der Chef :)
Radek
9

Zusätzlich zu Zapphods Antwort:

In meinem Fall war die Indizierung in der Tat falsch, da ich alle Migrationen und die Datenbank bei der Entwicklung wahrscheinlich 10 bis 15 Mal gelöscht hatte, da ich mich nicht in der Phase der Migration befand.

Ich habe einen IntegrityError aktiviert finished_product_template_finishedproduct_pkey

Indizieren Sie die Tabelle neu und starten Sie den Runserver neu:

Ich habe pgadmin3 verwendet und für jeden Index, der falsch war, und doppelte Schlüsselfehler constraintsausgelöst , habe ich zu und neu indiziert.

Geben Sie hier die Bildbeschreibung ein

Und dann neu indiziert.

Geben Sie hier die Bildbeschreibung ein

jmunsch
quelle
Reindex hat bei mir nicht funktioniert, aber Analyze Full (mit ebenfalls aktivierten Optionen zum Einfrieren und Analysieren) hat den Trick für mich getan. Ich hatte keine Sequenz oder keinen Trigger auf dem Tisch, aber ich glaube, irgendwo ist ein unfertiger Einsatz hängen geblieben, also hat Vacuum Full geholfen.
MrLehiste
5

Die Lösung besteht darin, dass Sie Ihre Primärschlüsselfelder erneut synchronisieren müssen, wie von "Hacking Life" gemeldet, der einen Beispiel-SQL-Code geschrieben hat. Wie von "Ad N" vorgeschlagen, ist es jedoch besser, den Befehl Django auszuführen sqlsequencereset, um den genauen SQL-Code zu erhalten, den Sie können kopieren und einfügen oder mit einem anderen Befehl ausführen.

Als weitere Verbesserung dieser Antworten würde ich Ihnen und anderen Lesern empfehlen, den SQL-Code nicht zu kopieren und einzufügen, sondern die SQL-Abfrage, die von sqlsequenceresetIhrem Python-Code generiert wurde, auf diese Weise (unter Verwendung der Standarddatenbank ) sicherer auszuführen. ::

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)

Ich habe diesen Code mit Python3.6 , Django 2.0 und PostgreSQL 10 getestet .

Paolo Melchiorre
quelle
Bitte posten Sie keine identischen Antworten auf mehrere Fragen . Veröffentlichen Sie eine gute Antwort und stimmen Sie ab, um die anderen Fragen als Duplikate zu schließen. Wenn die Frage kein Duplikat ist, passen Sie Ihre Antworten auf die Frage an .
Martijn Pieters
3

Wenn Sie die PK für alle Ihre Tabellen wie mich zurücksetzen möchten, können Sie die von PostgreSQL empfohlene Methode verwenden :

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;

Nach dem Ausführen dieser Abfrage müssen Sie die Ergebnisse der Abfrage ausführen. Normalerweise kopiere ich und füge es in den Editor ein. Dann finde und ersetze ich "SELECTmit SELECTund ;"mit ;. Ich kopiere und füge es in pgAdmin III ein und führe die Abfrage aus. Es setzt alle Tabellen in der Datenbank zurück. Weitere "professionelle" Anweisungen finden Sie unter dem obigen Link.

Bobort
quelle
2

Ich bin auf diesen Fehler gestoßen, weil ich zusätzliche Argumente falsch an die Speichermethode übergeben habe.

Versuchen Sie für alle, die darauf stoßen, UPDATE zu erzwingen mit:

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

Wenn Sie einen Fehler erhalten, den Sie nicht übergeben können, force_insertund force_updategleichzeitig übergeben Sie wahrscheinlich einige benutzerdefinierte Argumente falsch, wie ich es getan habe.

JacobF
quelle
Dies ist eine schöne Lösung innerhalb von Django. Importieren Sie einfach Ihre Modellklasse aus der Shell (wenn Sie shell_plus nicht verwenden) und führen Sie dann MyModelClass.objects.first () aus. Save (force_update = True)
Doug Bradshaw
1

Sie müssen nur zu pgAdmin III gehen und dort Ihr Skript mit dem Namen der Tabelle ausführen:

SELECT setval('tablename_id_seq', (SELECT MAX(id) FROM tablename)+1);
Luis Reales
quelle
0

Ich habe den gleichen Fehler wie beim OP erhalten.

Ich hatte einige Django-Modelle erstellt, eine Postgres-Tabelle basierend auf den Modellen erstellt und über Django Admin einige Zeilen zur Postgres-Tabelle hinzugefügt. Dann habe ich an einigen Spalten in den Modellen herumgespielt (Änderungen an ForeignKeys usw.), aber vergessen, die Änderungen zu migrieren.

Das Ausführen der Migrationsbefehle hat mein Problem gelöst, was angesichts der obigen SQL-Antworten sinnvoll ist.

Um zu sehen, welche Änderungen angewendet werden, ohne sie tatsächlich anzuwenden:
python manage.py makemigrations --dry-run --verbosity 3

Wenn Sie mit diesen Änderungen zufrieden sind, führen Sie Folgendes aus:
python manage.py makemigrations

Dann renne:
python manage.py migrate

Allardbrain
quelle
0

Ich bekam ein ähnliches Problem und nichts schien zu funktionieren. Wenn Sie die Daten benötigen (dh beim Dump nicht ausschließen können), stellen Sie sicher, dass Sie alle post_save-Empfänger deaktiviert (kommentiert) haben. Ich denke, die Daten würden importiert, aber aufgrund dieser würde es wieder dasselbe Modell erstellen. Hat für mich gearbeitet.

Josh
quelle