Mehrere Änderungen, ein Ergebnis · Übung
Die Verbindung über das Ergebnis entscheiden lassen
Die manuelle Variante hat zwei Zweige und zwei ausdrückliche Transaktionsentscheidungen. Eine SQLite-Verbindung kann dieselben Entscheidungen daraus ableiten, wie ein with-Block endet.
Die Form ist wichtig. Setze den Verbindungskontext innerhalb von try und halte den erwarteten Handler außerhalb davon:
try:
with connection:
connection.execute(INSERT_LOAN, first_loan)
connection.execute(INSERT_LOAN, second_loan)
except sqlite3.IntegrityError:
return False
return True
Verfolge jetzt die beiden Arten, auf die dieser Block enden kann. Der normale Weg erreicht True erst, nachdem der Kontext festgeschrieben hat. Auf dem IntegrityError-Weg rollt der Kontext zurück, bevor der äußere Handler False zurückgibt:

Wenn die Ausführung das Ende von with connection: normal erreicht, schreibt die Verbindung die offene Arbeit fest. Wenn eine Ausnahme den Block verlässt, rollt sie zurück, bevor die Ausnahme nach außen weiterläuft. Der äußere Handler erkennt dann sqlite3.IntegrityError und gibt False zurück.
Entferne bei diesem Umbau die manuellen Aufrufe von commit() und rollback(). Halte beide Einfügungen innerhalb eines Kontexts. Ein Kontext um jede einzelne Einfügung würde zwei Ergebnisse statt eines Paares erzeugen.
Der Handler darf nicht in with connection: verschoben werden. Wenn er den Integritätsfehler dort abfinge und normal zurückkehrte, würde der Kontext einen normalen Ausgang sehen. Er könnte die erste gültige Einfügung festschreiben und damit das Teilergebnis vom Anfang wiederherstellen.
Klicke auf Run. Die sichtbare Ausgabe bleibt dieselbe wie bei deiner manuellen Lösung: Gültige IDs erscheinen beim erneuten Öffnen als [7001, 7002], während der Versuch mit fehlendem Mitglied False zurückgibt und auf keiner Verbindung eine vorgeschlagene ID hinterlässt. Dieses Mal hat die Verbindung diese Ergebnisse aus dem Verlassen des Kontexts abgeleitet.
Submit schickt auch einen anderen Fehler durch die Funktion und verlangt, dass er nach außen weiterläuft. Fange nur den erwarteten Integritätsfall ab. Es prüft den Zustand über eine neue Verbindung und führt über die ursprüngliche eine weitere Abfrage aus.
Dieser Block with connection: ist ein Transaktionskontext. Anders als ein Dateikontext bedeutet er nicht „schließe dieses Objekt, wenn der Block endet“. Er entscheidet über das Ergebnis der offenen Transaktion. Bevor wir weitere Operationen hinzufügen, machen wir diesen Unterschied im Lebenszyklus sichtbar.
Aufgabe
Baue checkout_pair so um, dass ein einzelner Block with connection: beide Einfügungen umschließt. Setze try um diesen Block und fange nur sqlite3.IntegrityError ab, nachdem er verlassen wurde.
Entferne manuelle Commit- und Rollback-Aufrufe. Gib nach normalem Abschluss True und beim erwarteten Integritätsfehler False zurück.