Saya ingin mendapatkan objek dari database jika sudah ada (berdasarkan parameter yang disediakan) atau membuatnya jika tidak ada.
Django get_or_create
(atau sumber ) melakukan ini. Apakah ada cara pintas yang setara di SQLAlchemy?
Saat ini saya menuliskannya secara eksplisit seperti ini:
def get_or_create_instrument(session, serial_number):
instrument = session.query(Instrument).filter_by(serial_number=serial_number).first()
if instrument:
return instrument
else:
instrument = Instrument(serial_number)
session.add(instrument)
return instrument
python
django
sqlalchemy
FogleBird
sumber
sumber
session.merge
: stackoverflow.com/questions/12297156/…Jawaban:
Itu pada dasarnya cara untuk melakukannya, tidak ada jalan pintas yang tersedia AFAIK.
Anda dapat menggeneralisasikannya tentu saja:
sumber
try...except IntegrityError: instance = session.Query(...)
sekitarsession.add
blok.Mengikuti solusi @WoLpH, ini adalah kode yang berfungsi untuk saya (versi sederhana):
Dengan ini, saya bisa mendapatkan_atau membuat objek model saya.
Misalkan objek model saya adalah:
Untuk mendapatkan atau membuat objek saya, saya menulis:
sumber
commit
(atau setidaknya hanya menggunakan aflush
). Ini meninggalkan kontrol sesi kepada penelepon metode ini dan tidak akan mengambil risiko mengeluarkan komit prematur. Juga, menggunakanone_or_none()
bukannyafirst()
mungkin sedikit lebih aman.Saya telah bermain dengan masalah ini dan berakhir dengan solusi yang cukup kuat:
Saya hanya menulis posting blog yang cukup luas pada semua detail, tetapi beberapa ide mengapa saya menggunakan ini.
Itu membongkar ke sebuah tuple yang memberitahu Anda jika objek itu ada atau tidak. Ini sering berguna dalam alur kerja Anda.
Fungsi ini memberikan kemampuan untuk bekerja dengan
@classmethod
fungsi-fungsi pencipta yang didekorasi (dan atribut khusus untuk mereka).Solusinya melindungi terhadap Kondisi Ras ketika Anda memiliki lebih dari satu proses yang terhubung ke datastore.
EDIT: Saya sudah berubah
session.commit()
menjadisession.flush()
seperti yang dijelaskan dalam posting blog ini . Perhatikan bahwa keputusan ini khusus untuk datastore yang digunakan (Postgres dalam kasus ini).EDIT 2: Saya telah memperbarui menggunakan {} sebagai nilai default dalam fungsi karena ini adalah gotcha khas Python. Terima kasih atas komentarnya , Nigel! Jika Anda penasaran dengan gotcha ini, lihat pertanyaan StackOverflow ini dan posting blog ini .
sumber
get_or_create
adalah tidak benang-aman. Itu bukan atom. Juga, Djangoget_or_create
mengembalikan bendera Benar jika turunannya dibuat atau bendera Palsu sebaliknya.get_or_create
itu melakukan hal yang hampir sama persis. Solusi ini juga mengembalikanTrue/False
bendera ke sinyal jika objek dibuat atau diambil, dan juga bukan atom. Namun, keamanan thread dan pembaruan atom menjadi perhatian untuk database, bukan untuk Django, Flask atau SQLAlchemy, dan dalam kedua solusi ini dan Django, diselesaikan dengan transaksi pada database.IntegrityError
case kembaliFalse
karena klien ini tidak membuat objek?Versi modifikasi dari jawaban erik yang sangat baik
create_method
. Jika objek yang dibuat memiliki hubungan dan itu ditugaskan anggota melalui hubungan tersebut, itu secara otomatis ditambahkan ke sesi. Misalnya membuatbook
, yang memilikiuser_id
danuser
sebagai hubungan yang sesuai, maka melakukanbook.user=<user object>
di dalamcreate_method
akan menambahbook
sesi. Ini berarti bahwacreate_method
harus ada di dalamwith
untuk mendapatkan keuntungan dari rollback akhirnya. Perhatikan bahwabegin_nested
secara otomatis memicu flush.Perhatikan bahwa jika menggunakan MySQL, level isolasi transaksi harus diatur agar
READ COMMITTED
tidakREPEATABLE READ
berfungsi. Get_or_create Django (dan di sini ) menggunakan strategi yang sama, lihat juga dokumentasi Django .sumber
IntegrityError
ulang mungkin masih gagal denganNoResultFound
tingkat isolasi default MySQLREPEATABLE READ
jika sesi sebelumnya menanyakan model dalam transaksi yang sama. Solusi terbaik yang bisa saya buat adalah meneleponsession.commit()
sebelum permintaan ini, yang juga tidak ideal karena pengguna mungkin tidak mengharapkannya. Jawaban yang dirujuk tidak memiliki masalah ini karena session.rollback () memiliki efek yang sama untuk memulai transaksi baru.commit
di dalam fungsi ini bisa dibilang lebih buruk daripada melakukanrollback
, meskipun untuk kasus penggunaan khusus dapat diterima.commit()
sendiri. Jika pemahaman saya tentang kode itu benar, inilah yang dilakukan Django., so it does not look like they try to handle this. Looking at the [source](https://github.com/django/django/blob/master/django/db/models/query.py#L491) confirms this. I'm not sure I understand your reply, you mean the user should put his/her query in a nested transaction? It's not clear to me how a
pengaruh `BACA BERKOMITMEN BACA` yang dibaca denganREPEATABLE READ
. Jika tidak ada efek maka situasinya tampaknya tidak dapat diselamatkan, jika efek maka permintaan terakhir dapat disarangkan?READ COMMITED
, mungkin saya harus memikirkan kembali keputusan saya untuk tidak menyentuh default database. Saya telah menguji bahwa memulihkanSAVEPOINT
dari sebelum permintaan dibuat membuatnya seolah-olah permintaan itu tidak pernah diterimaREPEATABLE READ
. Oleh karena itu, saya merasa perlu melampirkan kueri dalam klausa coba dalam transaksi bersarang sehingga kueri dalamIntegrityError
klausa kecuali dapat bekerja sama sekali.Resep SQLALchemy ini melakukan pekerjaan dengan baik dan elegan.
Hal pertama yang harus dilakukan adalah mendefinisikan fungsi yang diberi Sesi untuk dikerjakan, dan mengaitkan kamus dengan Sesi () yang melacak kunci unik saat ini .
Contoh penggunaan fungsi ini adalah dalam mixin:
Dan akhirnya membuat model get_or_create unik:
Resepnya masuk lebih dalam ke ide dan memberikan pendekatan yang berbeda tetapi saya telah menggunakan ini dengan sukses besar.
sumber
Semantik terdekat mungkin:
tidak yakin bagaimana halal bergantung pada yang didefinisikan secara global
Session
dalam sqlalchemy, tetapi versi Django tidak mengambil koneksi jadi ...Tuple yang dikembalikan berisi instance dan boolean yang menunjukkan jika instance dibuat (yaitu False jika kita membaca instance dari db).
Django
get_or_create
sering digunakan untuk memastikan bahwa data global tersedia, jadi saya berkomitmen sedini mungkin.sumber
scoped_session
, yang harus menerapkan manajemen sesi thread-safe (apakah ini ada pada 2014?).Saya sedikit menyederhanakan @Kevin. solusi untuk menghindari pembungkus seluruh fungsi dalam
if
/else
pernyataan. Dengan cara ini hanya ada satureturn
, yang saya temukan lebih bersih:sumber
Tergantung pada tingkat isolasi yang Anda adopsi, tidak ada solusi di atas yang akan berfungsi. Solusi terbaik yang saya temukan adalah RAW SQL dalam bentuk berikut:
Ini aman secara transaksi apa pun tingkat isolasi dan tingkat paralelisme.
Hati-hati: untuk membuatnya efisien, akan lebih bijaksana untuk memiliki INDEX untuk kolom unik.
sumber