sqlalchemy flush () dan dimasukkan id?

114

Saya ingin melakukan sesuatu seperti ini:

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

Tapi f.idapakah Nonesaat saya mencobanya. Bagaimana saya bisa membuat ini bekerja?

Eloff
sumber
2
Dapatkah Anda menginisialisasi mesin SA dengan echo=True, dan melihat SQL apa yang dieksekusi pada saat bersamaan? Apa yang Anda jelaskan seharusnya berfungsi dan memberi Anda id, tetapi mungkin ada beberapa masalah lain yang menyebabkan f.id menjadi None.
Pavel Repin

Jawaban:

63

Kode sampel Anda seharusnya berfungsi sebagaimana mestinya. SQLAlchemy harus memberikan nilai untuk f.id, dengan asumsi kolom kunci primernya dibuat secara otomatis. Atribut kunci utama diisi segera dalam flush()proses saat dibuat, dan tidak ada panggilan ke yang commit()diperlukan. Jadi jawabannya di sini terletak pada satu atau beberapa hal berikut:

  1. Detail pemetaan Anda
  2. Jika ada kebiasaan aneh dari backend yang digunakan (seperti, SQLite tidak menghasilkan nilai integer untuk kunci utama komposit)
  3. Apa yang dikatakan SQL yang dipancarkan saat Anda mengaktifkan echo
zzzeek
sumber
1
Anda benar, pemeriksaan cepat di shell menunjukkan itu mengisi bidang kunci utama dengan nilai. Saya harus menyelidiki mengapa itu tidak berhasil dalam praktiknya.
Eloff
3
"kode sampel Anda harus memberikan nilai" terdengar seperti Anda mengatakan "Anda seharusnya memberikan nilai untuk id di tempat pertama", daripada "kode yang Anda miliki seharusnya berfungsi sebagaimana adanya". Menjadi jelas bagi saya bahwa yang Anda maksud adalah yang terakhir daripada yang pertama hanya setelah pembacaan ketiga. Bisakah Anda menjelaskan?
Stochastic
126

Saya baru saja mengalami masalah yang sama, dan setelah pengujian saya menemukan bahwa TIDAK ADA jawaban yang cukup.

Saat ini, atau pada sqlalchemy .6+, ada solusi yang sangat sederhana (saya tidak tahu apakah ini ada di versi sebelumnya, meskipun saya membayangkannya ada):

session.refresh ()

Jadi, kode Anda akan terlihat seperti ini:

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.

Begitulah cara melakukannya.

dpb
sumber
8
Ini semakin mendekati jawaban yang mungkin berhasil untuk saya, tetapi saya menerima kesalahan berikut: InvalidRequestError: Tidak dapat menyegarkan contoh '<....>'. Tampaknya instance tersebut sudah tidak ada lagi setelah penghapusan. Hargai wawasan apa pun.
PlaidFan
1
Anda baru saja menyelamatkan pantat saya. Saya rasa saya tidak akan pernah menggunakan ORM lagi yang berasal dari Django. Perintah flush () TIDAK berfungsi sebagai IMHO yang didokumentasikan.
Marc
2
Perbarui, saya harus menggunakan sessionmaker(autoflush=True), combo w / refresh () memberi saya ID baris. #grrr
Marc
5
Jika Anda tidak memahami perbedaan antara flush()dan commit(), berikut penjelasan yang bagus: stackoverflow.com/a/4202016/1252290
Epoc
2
@PlaidFan Alih-alih flush()digunakan commit()dan setelah itu - segarkan dengan session.refresh(f), berfungsi untuk saya, dan saya menggunakan versi SQLAlchemy0.6.7
Ricky Levi
20

Terima kasih untuk semuanya. Saya memecahkan masalah saya dengan memodifikasi pemetaan kolom. Bagi saya, autoincrement=Truediperlukan.

asal:

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

setelah diubah:

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

kemudian

session.flush()  
print(f.id)

baiklah!

poloxue
sumber
6

tidak seperti jawaban yang diberikan oleh dpb, penyegaran tidak diperlukan. setelah Anda menyiram, Anda dapat mengakses bidang id, sqlalchemy secara otomatis menyegarkan id yang dibuat secara otomatis di backend

Saya mengalami masalah ini dan menemukan alasan yang tepat setelah beberapa penyelidikan, model saya dibuat dengan id sebagai integerfield dan dalam bentuk saya id diwakili dengan hiddenfield (karena saya tidak ingin menunjukkan id dalam formulir saya). Bidang tersembunyi secara default direpresentasikan sebagai teks. setelah saya mengubah formulir menjadi integerfield dengan widget = hiddenInput ()) masalah terpecahkan.

Ryan
sumber
1
Seperti yang dinyatakan, hanya refresh () yang berfungsi untuk saya. Dalam melakukan migrasi data saya membutuhkan ID baris dalam satu lingkaran untuk mengisi FK. Saya mencoba setiap kombinasi dari commit, flush, session hacking dan refresh () adalah satu-satunya hal yang berhasil. Data saya sangat bersih dan saya baru saja menemukan bahwa SQLA tidak terlalu bagus (memiliki exp yang cukup dalam setidaknya 5 ORMS utama). Hanya membungkus 5+ jam mencoba mendapatkan id baris untuk add () -> commit () / flush ().
Marc
1

Saya pernah mengalami masalah dengan menetapkan 0ke id sebelum memanggil session.addmetode. ID ditetapkan dengan benar oleh database tetapi id yang benar tidak diambil dari sesi setelahnya session.flush().

babky
sumber
-7

Anda harus mencoba menggunakan, session.save_or_update(f)bukan session.add(f).

Mohit Ranka
sumber
1
save_or_updatesudah tidak digunakan lagi sejak 0,5 atau lebih. session.add()harus melakukannya.
Pavel Repin