Sqlalchemy: Aktualisierung der sekundären Beziehung

8

Ich habe zwei Tabellen, sagen A und B. Beide haben eine Primärschlüssel-ID. Sie haben eine Viele-zu-Viele-Beziehung, SEC.

%Vor%

Betrachten wir diesen Code.

%Vor%

Manchmal bekomme ich in der letzten Zeile einen Fehler mit

%Vor%

Nach meinem Verständnis versuche ich, (a.id, b.id) wieder in die 'sec' -Tabelle einzufügen, was zu einem eindeutigen Constraint-Fehler führt. Ist es das, was es ist? Wenn ja, wie kann ich das vermeiden? Wenn nicht, warum habe ich diesen Fehler?

    
Sri 08.10.2012, 18:33
quelle

2 Antworten

8

Das Problem besteht darin, dass Sie sicherstellen möchten, dass die von Ihnen erstellten Instanzen eindeutig sind. Wir können einen alternativen Konstruktor erstellen, der einen Cache von vorhandenen nicht-committeten Instanzen überprüft oder die Datenbank nach einer vorhandenen festgeschriebenen Instanz abfragt, bevor eine neue Instanz zurückgegeben wird.

Hier ist eine Demonstration einer solchen Methode:

%Vor%

Die create_unique -Methode wurde vom Beispiel aus dem SQLAlchemy-Wiki inspiriert. Diese Version ist viel weniger verschachtelt, Einfachheit gegenüber Flexibilität bevorzugen. Ich habe es in Produktionssystemen ohne Probleme verwendet.

Es gibt offensichtlich Verbesserungen, die hinzugefügt werden können; Das ist nur ein einfaches Beispiel. Die Methode get_unique könnte von einem UniqueMixin geerbt werden, um für eine beliebige Anzahl von Modellen verwendet zu werden. Eine flexiblere Formulierung von Argumenten könnte implementiert werden. Dies beseitigt auch das Problem, dass mehrere Threads widersprüchliche Daten einfügen, die von Ants Aasma erwähnt werden; Handhabung, die komplexer ist, sollte aber eine offensichtliche Erweiterung sein. Das überlasse ich dir.

    
davidism 30.12.2013 07:11
quelle
3

Der von Ihnen erwähnte Fehler liegt in der Tat darin, einen widersprüchlichen Wert in die sec-Tabelle einzufügen. Um sicher zu sein, dass es sich bei der Operation, die Sie denken, um eine vorherige Änderung handelt, aktivieren Sie die SQL-Protokollierung und prüfen Sie, welche Werte eingefügt werden sollen, bevor ein Fehler auftritt.

Beim Überschreiben eines Viele-zu-Viele-Sammlungswerts vergleicht SQLAlchemy den neuen Inhalt der Sammlung mit dem Status in der Datenbank und gibt dementsprechend Lösch- und Einfügeanweisungen aus. Wenn Sie nicht in SQLAlchemy-Interna herumstöbern, sollten Sie auf zwei Arten auf diesen Fehler stoßen.

Zuerst ist die gleichzeitige Änderung: Prozess 1 holt den Wert a.rels und bemerkt, dass er leer ist, während Prozess 2 auch a.rels abruft, auf [b1, b2] setzt und das Löschen der (a, b1) festlegt, (a, b2) Tupel, Prozess 1 setzt a.rels auf [b1, b3] und bemerkt, dass der vorherige Inhalt leer war und wenn er versucht, das zweite Tupel (a, b1) zu leeren, wird ein doppelter Schlüsselfehler angezeigt. Die richtige Aktion in solchen Fällen ist normalerweise, die Transaktion von oben zu wiederholen. Sie können stattdessen die serialisierbare Transaktionsisolierung verwenden, um stattdessen einen Serialisierungsfehler zu erhalten Fall, der sich von einem Geschäftslogikfehler unterscheidet, der einen doppelten Schlüsselfehler verursacht.

Der zweite Fall tritt auf, wenn Sie es geschafft haben, SQLAlchemy davon zu überzeugen, dass Sie den Datenbankzustand nicht kennen müssen, indem Sie die Ladestrategie des rels-Attributs auf noload setzen. Dies kann beim Definieren der Beziehung durch Hinzufügen des Parameters lazy='noload' oder beim Abfragen von .options(noload(A.rels)) für die Abfrage erfolgen. SQLAlchemy wird annehmen, dass die sec-Tabelle keine übereinstimmenden Zeilen für Objekte enthält, die mit dieser Strategie geladen sind.

    
Ants Aasma 08.10.2012 21:46
quelle