Bagaimana cara memperbarui entri baris SQLAlchemy?

137

Asumsikan tabel memiliki tiga kolom: username, passworddan no_of_logins.

Ketika pengguna mencoba masuk, itu diperiksa untuk entri dengan pertanyaan seperti

user = User.query.filter_by(username=form.username.data).first()

Jika kata sandi cocok, ia melanjutkan. Yang ingin saya lakukan adalah menghitung berapa kali pengguna login. Jadi, setiap kali ia berhasil masuk, saya ingin menambah no_of_loginsbidang dan menyimpannya kembali ke tabel pengguna. Saya tidak yakin bagaimana menjalankan permintaan pembaruan dengan SqlAlchemy.

webminal.org
sumber

Jawaban:

132
user.no_of_logins += 1
session.commit()
Denis
sumber
12
stackoverflow.com/a/2334917/125507 mengatakan ini adalah cara yang buruk untuk melakukannya. Bagaimana jika barisnya belum ada?
endolith
6
Sesuai tautan endolith, user.no_of_logins += 1dapat menciptakan kondisi balapan. Sebaliknya gunakan user.no_of_logins = user.no_of_logins + 1. Diterjemahkan ke dalam sql cara yang benar yang terakhir menjadi: SET no_of_logins = no_of_logins + 1.
ChaimG
5
@ ChaimG Saya kira maksud Anda user.no_of_logins = User.no_of_logins + 1, atau dengan kata lain menggunakan atribut instrumented model untuk menghasilkan ekspresi SQL. Karena komentar Anda menampilkan 2 cara untuk menghasilkan kondisi balapan yang sama.
Ilja Everilä
2
Mereka tidak. Yang pertama mengatur atribut ke ekspresi SQL, yang terakhir melakukan penambahan di tempat dengan Python dan kembali memperkenalkan balapan.
Ilja Everilä
1
@ Jayrizzo Saya tidak begitu akrab dengan tingkat isolasi transaksi SERIALIZABLE, tetapi menurut pemahaman saya itu akan memungkinkan melakukan penambahan dalam Python menggunakan penambahan di tempat. Jika ada perlombaan, salah satu transaksi kemudian akan berhasil dan yang lain akan gagal dan harus coba lagi (dengan status baru DB). Tapi saya mungkin salah paham SERIALIZABLE.
Ilja Everilä
343

Ada beberapa cara untuk UPDATEmenggunakannyasqlalchemy

1) user.no_of_logins += 1
   session.commit()

2) session.query().\
       filter(User.username == form.username.data).\
       update({"no_of_logins": (User.no_of_logins +1)})
   session.commit()

3) conn = engine.connect()
   stmt = User.update().\
       values(no_of_logins=(User.no_of_logins + 1)).\
       where(User.username == form.username.data)
   conn.execute(stmt)

4) setattr(user, 'no_of_logins', user.no_of_logins+1)
   session.commit()
Nima Soroush
sumber
3
Saya menggunakan setattr(query_result, key, value)untuk beberapa pembaruan dalam Flask SQLAlchemy diikuti oleh komit. Ada alasan untuk mendiskon pola ini?
Marc
2
@datamafia: Anda dapat menggunakan setattr(query_result, key, value), yang persis sama dengan menulisquery_result.key = value
Nima Soroush
Thx @NimaSoroush, itu yang saya lakukan dan ya setara.
Marc
8
Apakah mungkin untuk menjelaskan perbedaan antara kasus-kasus ini? Terima kasih!
Hatshepsut
1
@Hatshepsut Satu perbedaan yang saya temui hari ini antara 4 dan 1/2 adalah di: groups.google.com/forum/#!topic/sqlalchemy/wGUuAy27otM
Vikas Prasad
13

Contoh untuk memperjelas masalah penting dalam komentar jawaban yang diterima

Saya tidak mengerti sampai saya bermain-main dengan itu sendiri, jadi saya pikir akan ada orang lain yang bingung juga. Katakanlah Anda bekerja pada pengguna yang id == 6dan yang no_of_logins == 30ketika Anda mulai.

# 1 (bad)
user.no_of_logins += 1
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 2 (bad)
user.no_of_logins = user.no_of_logins + 1
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 3 (bad)
setattr(user, 'no_of_logins', user.no_of_logins + 1)
# result: UPDATE user SET no_of_logins = 31 WHERE user.id = 6

# 4 (ok)
user.no_of_logins = User.no_of_logins + 1
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

# 5 (ok)
setattr(user, 'no_of_logins', User.no_of_logins + 1)
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

Inti nya

Dengan mereferensikan kelas alih-alih instance, Anda bisa membuat SQLAlchemy menjadi lebih pintar tentang penambahan, membuatnya terjadi di sisi basis data alih-alih sisi Python. Melakukannya di dalam basis data lebih baik karena kurang rentan terhadap korupsi data (misalnya, dua klien berupaya untuk meningkatkan secara bersamaan dengan hasil bersih hanya satu peningkatan daripada dua). Saya menganggap itu mungkin untuk melakukan penambahan dengan Python jika Anda mengatur kunci atau meningkatkan level isolasi, tetapi mengapa repot-repot jika Anda tidak harus melakukannya?

Peringatan

Jika Anda akan bertambah dua kali melalui kode yang menghasilkan SQL like SET no_of_logins = no_of_logins + 1, maka Anda harus melakukan atau setidaknya menyiram di antara kenaikan, atau Anda hanya akan mendapatkan satu kenaikan secara total:

# 6 (bad)
user.no_of_logins = User.no_of_logins + 1
user.no_of_logins = User.no_of_logins + 1
session.commit()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6

# 7 (ok)
user.no_of_logins = User.no_of_logins + 1
session.flush()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6
user.no_of_logins = User.no_of_logins + 1
session.commit()
# result: UPDATE user SET no_of_logins = no_of_logins + 1 WHERE user.id = 6
MarredCheese
sumber
Halo, terima kasih atas jawaban Anda, daripada variabel int yang saya coba perbarui variabel string, bagaimana saya melakukannya? saya menggunakan database sqlite dan variabel yang ingin saya ubah ada di current_user, melalui mengirimkan formulir
dani bilel
1
@danibilel Lihat dokumentasinya, yang cukup menyeluruh. Bagian tutorial ini membahas pembaruan bidang string: docs.sqlalchemy.org/en/13/orm/… . Kalau tidak, saya sarankan Anda memposting pertanyaan baru yang menunjukkan apa yang Anda terjebak secara khusus.
MarredCheese
Terima kasih atas tautannya, setelah saya menghabiskan seharian mencari, saya tahu masalah saya adalah dengan db.session.commit (), itu tidak bertahan pada perubahan di konsol sebagaimana mestinya, saya menemukan pertanyaan serupa tetapi memberikan jawaban tidak bekerja untuk saya, dalam aplikasi web yang sebenarnya bahkan lebih buruk! perubahan tidak disimpan sama sekali, maaf untuk komentar panjang tetapi saya tidak dapat menambahkan pertanyaan karena kebijakan situs web, bantuan apa pun akan dihargai.
dani bilel
4

Dengan bantuan user=User.query.filter_by(username=form.username.data).first()pernyataan, Anda akan mendapatkan pengguna yang ditentukan dalam uservariabel.

Sekarang Anda dapat mengubah nilai variabel objek baru seperti user.no_of_logins += 1dan menyimpan perubahan dengan sessionmetode komit.

Nilesh
sumber
2

Saya menulis bot telegram, dan memiliki beberapa masalah dengan baris pembaruan. Gunakan contoh ini, jika Anda memiliki Model

def update_state(chat_id, state):
    try:
        value = Users.query.filter(Users.chat_id == str(chat_id)).first()
        value.state = str(state)
        db.session.flush()
        db.session.commit()
        #db.session.close()
    except:
        print('Error in def update_state')

Mengapa menggunakan db.session.flush()? Itu sebabnya >>> SQLAlchemy: Apa perbedaan antara flush () dan commit ()?

Andrew Lutsyuk
sumber
7
Siram sepenuhnya berlebihan sebelum komit. Komit selalu tersirat secara implisit. Seperti yang banyak dikatakan dalam tanya jawab yang Anda tautkan ke: " flush()selalu dipanggil sebagai bagian dari panggilan untuk commit()"
Ilja Everilä