sqlalchemy flush () e ottenere l'ID inserito?


114

Voglio fare qualcosa del genere:

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()

Ma f.idè Nonequando lo provo. Come posso farlo funzionare?


2
Puoi inizializzare il motore SA con echo=Truee vedere quale SQL viene eseguito al momento dello scaricamento? Quello che descrivi dovrebbe funzionare e darti l'id, ma potrebbe esserci qualche altro problema che fa sì che f.id sia Nessuno.
Pavel Repin

Risposte:


63

Il codice di esempio dovrebbe aver funzionato così com'è. SQLAlchemy dovrebbe fornire un valore per f.id, supponendo che sia una colonna di chiave primaria a generazione automatica . Gli attributi della chiave primaria vengono popolati immediatamente all'interno del flush()processo non appena vengono generati e non commit()dovrebbe essere richiesta alcuna chiamata a . Quindi la risposta qui sta in uno o più dei seguenti:

  1. I dettagli della tua mappatura
  2. Se ci sono stranezze del backend in uso (ad esempio, SQLite non genera valori interi per una chiave primaria composta)
  3. Cosa dice l'SQL emesso quando attivi l'eco

1
Hai ragione, un rapido controllo nella shell mostra che popola il campo della chiave primaria con un valore. Dovrò indagare sul motivo per cui non funzionava nella pratica.
Eloff

3
"il tuo codice di esempio dovrebbe fornire un valore" sembra che tu stia dicendo "avresti dovuto dare un valore per id in primo luogo", invece di "quel codice che hai avrebbe dovuto funzionare così com'è". Mi è apparso chiaro che intendevi il secondo piuttosto che il primo solo dopo la terza lettura. Potresti chiarire?
stocastico

126

Ho appena riscontrato lo stesso problema e dopo i test ho scoperto che NESSUNA di queste risposte è sufficiente.

Attualmente, o come da sqlalchemy .6+, c'è una soluzione molto semplice (non so se esiste nella versione precedente, anche se immagino che lo sia):

session.refresh ()

Quindi, il tuo codice sarebbe simile a questo:

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.

Ecco come farlo.


8
Questo si sta avvicinando a una risposta che potrebbe funzionare per me, ma ricevo il seguente errore: InvalidRequestError: Impossibile aggiornare l'istanza "<....>". Dopo il lavaggio appare che l'istanza semplicemente non esiste più. Apprezzo qualsiasi intuizione.
PlaidFan

1
Mi hai appena salvato il culo. Non credo che userò mai più l'ORM proveniente da Django. Il comando flush () NON funziona come IMHO documentato.
Marc

2
Aggiornamento, ho dovuto usare sessionmaker(autoflush=True), quella combinazione con refresh () mi ha fornito l'ID riga. #grrr
Marc

5
Se non capisci la differenza tra flush()e commit(), ecco una buona spiegazione: stackoverflow.com/a/4202016/1252290
Epoc

2
@PlaidFan Invece di flush()usarlo commit()e subito dopo - aggiorna con session.refresh(f), funziona per me, e io uso la versione SQLAlchemy0.6.7
Ricky Levi

20

Grazie a tutti. Ho risolto il mio problema modificando la mappatura delle colonne. Per me autoincrement=Trueè necessario.

origine:

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

dopo la modifica:

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

poi

session.flush()  
print(f.id)

va bene!


6

a differenza della risposta data da dpb, non è necessario un aggiornamento. una volta scaricato, puoi accedere al campo id, sqlalchemy aggiorna automaticamente l'id che viene generato automaticamente nel backend

Ho riscontrato questo problema e ho capito il motivo esatto dopo alcune indagini, il mio modello è stato creato con id come integerfield e nel mio modulo l'id è stato rappresentato con hiddenfield (poiché non volevo mostrare l'id nel mio modulo). Il campo nascosto è rappresentato per impostazione predefinita come testo. una volta cambiato il modulo in integerfield con widget = hiddenInput ()) il problema è stato risolto.


1
Come affermato, solo refresh () ha funzionato per me. Durante la migrazione dei dati avevo bisogno dell'ID riga in un ciclo per popolare un FK. Ho provato ogni combinazione di commit, flush, session hacking e refresh () era l'unica cosa che funzionava. I miei dati sono super puliti e sto solo scoprendo che SQLA non è davvero così buono (con una notevole esperienza in almeno 5 ORMS principali). Riavvolgendo più di 5 ore cercando di ottenere l'id di riga per un add () -> commit () / flush ().
Marc

1

Una volta ho avuto un problema con l'assegnazione 0di ID prima di chiamare il session.addmetodo. L'id è stato assegnato correttamente dal database ma l'ID corretto non è stato recuperato dalla sessione successiva session.flush().


-7

Dovresti provare a usare session.save_or_update(f)invece di session.add(f).


1
save_or_updateè stato deprecato dalla 0,5 circa. session.add()dovrebbe farlo.
Pavel Repin
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.