[Django] Neuen One to Many Datensatz speichern

Django, Flask, Bottle, WSGI, CGI…
Antworten
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Hallo Forum,

meines Wissens nach soll man 1:n Bzeihungen wie folgt speichern:

Code: Alles auswählen

class Lieferschein(models.Model):
    # ...

class LieferscheinPosition(models.Model):
    lieferschein = models.ForeignKey(Lieferschein)
Das Schöne ist, dass man auch über den Lieferschein dank der Django-Magie gut an die Positionen kommt, nämlich über: Lieferschein.lieferscheinpositionen_set
Das funktioniert super. Man kann darüber auch problemlos Positionen hinzufügen: Lieferschein.lieferscheinposition_set.add(LieferscheinPosition)

Was mache ich aber wenn ich einen neuen Lieferschein anlege? Das ist etwas anders als die Reporter->Artikel-Beispiele im Internet, weil der Lieferschein und seine Positionen ja quasi zeitgleich anfallen.

Ich hatte gehofft, dass ich das in einem Rutsch machen kann:

Code: Alles auswählen

l = Lieferschein()
lp = LieferscheinPosition()
l.lieferscheinposition_set.add(lp)
l.save()
Das funktioniert leider nicht. Beim Hinzufügen der Position gibt es einen Fehler, dass die id des Lieferscheins in der Lieferscheinpositon nicht null sein darf. Es scheint zu funktionieren wenn ich den Lieferschein, der ja eigentlich nur den "Kopf" repräsentiert, vorher speichere. Wenn dann aber etwas beim Speichern der Positionen ein Fehler auftritt, habe ich möglicherweise das Problem von Dateninkonsistenz, weil der Kopf da ist, die Positionen aber fehlen.
Gibt es hier eine gängige Möglichkeit?

Ich habe versucht für den entsprechenden View das Managment der Transaktion selbst zu lösen, aber das beschwört andere Probleme herauf. Zumindest bekomme ich immer beim Aufruf des Views den Fehler, das das die Transaktion noch aussteht (pending), obwohl es nur ein Ende der Funktion gibt und direkt davor ein Rollback ausgeführt wird.

Gruß
Sparrow
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo sparrow,

1-to-n Verknüpfungen werden in einer relationalen Datenbank, dass der n-Teil die id des 1-Teils in einem
Feld speichert. So lange Dein Lieferschein noch nicht gespeichert ist, hat er auch noch keine id und kann
deshalb nicht verknüpft werden.

Wie hast Du denn Deine Transaktionen von Hand gesteuert?

Code: Alles auswählen

from django.db import transaction

with transaction.commit_on_success():
    l = Lieferschein()
    l.save() # jetzt hat l auch eine ID
    lp = LieferscheinPosition(lieferschein=l)
    lp.save()
Grüße
Sirius
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Hah, dein Beitrag hat mir den richtigen Hinweis gegeben.

Ich habe das mit der Transaktion per Decorator versucht: @commit_manually. Bei dem commit_on_success bin ich mir immer unsicher unter welchen Umständen wirklich ein Rollback ausgelöst wird, weil ich die Fehler alle abfange um die letztendlich ausgelieferte Seite anzupassen.
Das hat nicht funktioniert. Warum habe ich nicht verstanden.

Ich habe den entsprechenden Bereich jetzt mit einem with transaction.commit.manually() in einen Kontext-Block verpackt. Das funktioniert wunderbar.

Danke!
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Hallo sparrow,

commit_on_success braucht natürlich die Exception für den Rollback. Deshalb darfst Du Fehler,
die zum Abbruch des Datenbankeintrags führen, nur außerhalb des with-Blocks abfangen.
Manuelles Commit verkompliziert die Sache dahingegen, dass Du für jeden Zweig Deiner
Fehlerbehandlung eine eigenes rollback brauchst.

Grüße
Sirius
Antworten