sqlalchemy flush () und ID eingefügt bekommen?

114

Ich möchte so etwas machen:

f = Foo(bar='x')
session.add(f)
session.flush()

# do additional queries using f.id before commit()
print f.id # should be not None

session.commit()

Aber f.idist , Nonewenn ich es versuchen. Wie kann ich das zum Laufen bringen?

Eloff
quelle
2
Können Sie die SA-Engine mit initialisieren echo=Trueund sehen, welche SQL beim Flush ausgeführt wird? Was Sie beschreiben, sollte funktionieren und Ihnen die ID geben, aber möglicherweise gibt es ein anderes Problem, das dazu führt, dass f.id None ist.
Pavel Repin

Antworten:

63

Ihr Beispielcode sollte so funktionieren, wie er ist. SQLAlchemy sollte einen Wert für bereitstellen f.id, vorausgesetzt, es handelt sich um eine automatisch generierende Primärschlüsselspalte. Primärschlüsselattribute werden sofort innerhalb des flush()Prozesses beim Generieren ausgefüllt, und es sollte kein Aufruf von commit()erforderlich sein. Die Antwort hier liegt also in einer oder mehreren der folgenden Aussagen:

  1. Die Details Ihres Mappings
  2. Wenn das Backend merkwürdige Macken aufweist (z. B. generiert SQLite keine ganzzahligen Werte für einen zusammengesetzten Primärschlüssel).
  3. Was die ausgegebene SQL sagt, wenn Sie Echo aktivieren
zzzeek
quelle
1
Sie haben Recht, eine schnelle Überprüfung in der Shell zeigt, dass das Primärschlüsselfeld mit einem Wert gefüllt ist. Ich muss untersuchen, warum es in der Praxis nicht funktioniert hat.
Eloff
3
"Ihr Beispielcode sollte einen Wert liefern" klingt so, als würden Sie sagen "Sie hätten zuerst einen Wert für id angeben sollen" und nicht "der Code, den Sie haben sollten, sollte so funktionieren, wie er ist". Mir wurde klar, dass Sie erst nach der dritten Lesung eher Letzteres als Ersteres meinten. Könnten Sie das klarstellen?
stochastischer
126

Ich bin gerade auf dasselbe Problem gestoßen, und nach dem Testen habe ich festgestellt, dass KEINE dieser Antworten ausreicht.

Derzeit oder ab sqlalchemy .6+ gibt es eine sehr einfache Lösung (ich weiß nicht, ob dies in einer früheren Version vorhanden ist, obwohl ich mir das vorstelle):

session.refresh ()

Ihr Code würde also ungefähr so ​​aussehen:

f = Foo(bar=x)
session.add(f)
session.flush()
# At this point, the object f has been pushed to the DB, 
# and has been automatically assigned a unique primary key id

f.id
# is None

session.refresh(f)
# refresh updates given object in the session with its state in the DB
# (and can also only refresh certain attributes - search for documentation)

f.id
# is the automatically assigned primary key ID given in the database.

So geht das.

dpb
quelle
8
Dies nähert sich einer Antwort, die für mich möglicherweise funktioniert, aber ich erhalte die folgende Fehlermeldung: InvalidRequestError: Instanz '<....>' konnte nicht aktualisiert werden. Nach dem Flush scheint die Instanz einfach nicht mehr zu existieren. Schätzen Sie jeden Einblick.
PlaidFan
1
Du hast gerade meinen Arsch gerettet. Ich glaube nicht, dass ich jemals wieder das ORM von Django verwenden werde. Der Befehl flush () funktioniert IMHO NICHT wie dokumentiert.
Marc
2
Update, das ich verwenden musste sessionmaker(autoflush=True), diese Kombination mit refresh () lieferte mir die Zeilen-ID. #grrr
Marc
5
Wenn Sie den Unterschied zwischen flush()und nicht verstehen commit(), ist hier eine gute Erklärung: stackoverflow.com/a/4202016/1252290
Epoc
2
@PlaidFan Anstatt zu flush()verwenden commit()und direkt danach - aktualisieren Sie es mit session.refresh(f), funktioniert für mich, und ich verwende die SQLAlchemy-Version0.6.7
Ricky Levi
20

Danke für alle. Ich habe mein Problem gelöst, indem ich die Spaltenzuordnung geändert habe. Für mich autoincrement=Trueist erforderlich.

Ursprung:

id = Column('ID', Integer, primary_key=True, nullable=False)

nach geändert:

id = Column('ID', Integer, primary_key=True, autoincrement=True, nullable=True)

dann

session.flush()  
print(f.id)

ist in Ordnung!

Poloxue
quelle
6

Im Gegensatz zu der Antwort von dpb ist eine Aktualisierung nicht erforderlich. Sobald Sie geleert haben, können Sie auf das ID-Feld zugreifen. sqlalchemy aktualisiert automatisch die ID, die im Backend automatisch generiert wird

Ich bin auf dieses Problem gestoßen und habe nach einigen Untersuchungen den genauen Grund herausgefunden. Mein Modell wurde mit der ID als Ganzzahlfeld erstellt und in meinem Formular wurde die ID mit verstecktem Feld dargestellt (da ich die ID nicht in meinem Formular anzeigen wollte). Das ausgeblendete Feld wird standardmäßig als Text dargestellt. Nachdem ich das Formular mit widget = hiddenInput () in integerfield geändert hatte, war das Problem gelöst.

Ryan
quelle
1
Wie gesagt hat nur refresh () für mich funktioniert. Bei einer Datenmigration benötigte ich die Zeilen-ID in einer Schleife, um einen FK zu füllen. Ich habe jede Kombination aus Commit, Flush, Session Hacking und Refresh () ausprobiert. Meine Daten sind super sauber und ich finde nur, dass SQLA nicht wirklich so gut ist (mit beträchtlicher Erfahrung in mindestens 5 großen ORMS). Einfach mehr als 5 Stunden einpacken und versuchen, die Zeilen-ID für add () -> commit () / flush () zu erhalten.
Marc
1

Ich hatte einmal ein Problem mit der Zuweisung 0der ID vor dem Aufruf der session.addMethode. Die ID wurde von der Datenbank korrekt zugewiesen, aber die richtige ID wurde danach nicht mehr aus der Sitzung abgerufen session.flush().

babky
quelle
-7

Sie sollten versuchen, session.save_or_update(f)anstelle von zu verwenden session.add(f).

Mohit Ranka
quelle
1
save_or_updatewurde seit ungefähr 0,5 veraltet. session.add()Sollte es tun.
Pavel Repin