it-swarm.com.de

Der doppelte Schlüsselwert von IntegrityError verstößt gegen die eindeutige Einschränkung - Django/Postgres

Ich verfolge in Bezug auf eine Frage, die ich zuvor gestellt habe , in der ich eine Konvertierung von einer doof/schlecht geschriebenen Mysql-Abfrage zu Postgresql suchte. Ich glaube, das ist mir gelungen. Wie auch immer, ich verwende Daten, die manuell aus 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 der Übermittlung bekomme ich eine Fehlermeldung von Django, die besagt:

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

Ich habe mir einige der Antworten angeschaut, die hier veröffentlicht wurden, und ich habe die Lösung für mein Problem nicht ganz gefunden (obwohl die damit verbundenen Fragen zu einer interessanten Lektüre geführt haben). Ich sehe das in meinen Protokollen, was interessant ist, weil ich niemals explizit Insert-Django aufrufen muss:

   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 tatsächliche 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 so überprüft und sie scheinen in Ordnung zu sein. Zu diesem Zeitpunkt bin ich nicht sicher, was ich tun soll - ich gehe davon aus, dass es etwas zu Djangos Ende ist, aber ich bin mir nicht sicher. Über Feedback würden wir uns sehr freuen!

49
the_man_slim

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

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

Es scheint sich um einen bekannten Verhaltensunterschied zwischen MySQL und SQLite zu handeln (sie aktualisieren den nächsten verfügbaren Primärschlüssel, selbst wenn ein Objekt mit einer expliziten ID eingefügt wird), Backends und andere Backends wie Postgres, Oracle, ... (sie tun dies nicht). .

Es gibt ein Ticket, das dasselbe Problem beschreibt . Obwohl es als ungültig geschlossen wurde, gibt es einen Hinweis, dass es einen Django-Verwaltungsbefehl gibt, um den nächsten verfügbaren Schlüssel zu aktualisieren.

So zeigen Sie die SQL-Aktualisierung aller nächster 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 Verwaltungsbefehl dbshell angeben. 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 entfernt wird, sodass es auch dann funktioniert, wenn die Migration zu einem anderen Backend erfolgt.

16
Ad N

Neben Zapphoden antworten:

In meinem Fall war die Indizierung in der Tat falsch, da ich alle Migrationen gelöscht hatte und die Datenbank beim Entwickeln wahrscheinlich 10 bis 15 Mal gelöscht wurde, da ich gerade nicht in der Migrationsphase war.

Ich habe einen IntegrityError für finished_product_template_finishedproduct_pkey erhalten. 

Tabelle neu indizieren und runserver neu starten:

Ich benutzte pgadmin3 und für welchen Index auch immer der falsche war und ich habe doppelte Schlüsselfehler geworfen, habe ich zur constraints navigiert und neu indexiert.

enter image description here

Und dann neu indiziert.

enter image description here

7
jmunsch

Ich hatte das gleiche Problem. Ich hatte eine vorhandene Tabelle in meiner "Inventar" -App und wollte neue Einträge in Django-Admin hinzufügen, und ich habe folgende Nachrichten erhalten:

Der doppelte Schlüsselwert verstößt gegen die eindeutige Einschränkung "inventory_part_pkey" DETAIL: Schlüssel (part_id) = (1) ist bereits vorhanden.

Wie bereits erwähnt, führen Sie den folgenden Code aus, um einen SQL-Befehl zum Zurücksetzen der ID-s zu generieren:

python manage.py sqlsequencereset inventory

In meinem Fall funktionierte python manage.py sqlsequencereset MyApp | python manage.py dbshell Nicht

  • Also habe ich die generierte SQL-Anweisung kopiert.
  • Dann öffnete ich pgAdmin für postgreSQL und öffnete meine Datenbank.
  • Auf das 6. Symbol geklickt (beliebige SQL-Abfragen ausführen)
  • Die Anweisung wurde kopiert, was generiert wurde.

In meinem Fall war es:

START; SELECT setval (pg_get_serial_sequence ('"inventory_signup"', 'id'), Koaleszierung (max ("id"), 1), max ("id") IS NICHT null) FROM "inventory_signup"; SELECT setval (pg_get_serial_sequence ('"inventory_supplier"', 'id'), coalesce (max ("id"), 1), max ("id") IS NICHT null) FROM "inventory_supplier"; VERPFLICHTEN;

Mit F5 ausgeführt.

Dies behebte alle meine Tabellen und das Hinzufügen neuer Datensätze am Ende, ohne es zu id = 1 hinzuzufügen.

6
Jozsef Turi

Die Lösung ist, 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 Django-Befehl sqlsequencereset auszuführen, um den genauen SQL-Code zu erhalten, den Sie erhalten kann kopieren und übergehen oder mit einem anderen Befehl ausführen.

Als weitere Verbesserung dieser Antworten schlage ich Ihnen und anderen Lesern vor, den SQL-Code nicht zu kopieren und einzufügen, sondern die von sqlsequencereset erzeugte SQL-Abfrage sicherer aus Ihrem Python-Code auf diese Weise auszuführen (mit dem Standarddatenbank):

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.

3

Wenn Sie die Datenbanken manuell kopiert haben, stoßen Sie möglicherweise auf die hier beschriebene Ausgabe von .

3
zaphod

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

Für jeden, dem dies begegnet, versuchen Sie UPDATE mit:

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

Wenn Sie eine Fehlermeldung erhalten, die Sie nicht gleichzeitig force_insert und force_update übergeben können, übergeben Sie wahrscheinlich einige benutzerdefinierte Argumente falsch, wie ich es tat.

2
jvannistelrooy

Wenn Sie den PK in allen Ihren Tabellen wie ich 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 und füge ich sie in den Editor ein. Dann finde und ersetze ich "SELECT durch SELECT und ;" durch ;. Ich kopiere und füge 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 Link oben.

1
Bobort

Ich habe den gleichen Fehler wie das 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 fummelte ich mit einigen der Spalten in den Modellen (wechselte um ForeignKeys usw.), hatte aber vergessen, die Änderungen zu migrieren.

Das Ausführen der Migrationsbefehle löste mein Problem, was angesichts der SQL-Antworten sinnvoll ist.

Um zu sehen, welche Änderungen angewendet werden würden, 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

0
allardbrain