จะอัพเดตรายการแถว SQLAlchemy ได้อย่างไร?


139

ตารางสมมติมีสามคอลัมน์: username, และpasswordno_of_logins

เมื่อผู้ใช้พยายามเข้าสู่ระบบระบบจะตรวจสอบรายการที่มีข้อความค้นหาเช่น

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

หากรหัสผ่านตรงกันเขาจะดำเนินการต่อไป สิ่งที่ฉันต้องการจะทำคือนับจำนวนครั้งที่ผู้ใช้เข้าสู่ระบบดังนั้นเมื่อใดก็ตามที่เข้าสู่ระบบสำเร็จฉันต้องการเพิ่มno_of_loginsฟิลด์และเก็บกลับไปที่ตารางผู้ใช้ ฉันไม่แน่ใจว่าจะเรียกใช้ update query กับ SqlAlchemy ได้อย่างไร

คำตอบ:


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

12
stackoverflow.com/a/2334917/125507บอกว่านี่เป็นวิธีที่ไม่ดี จะเกิดอะไรขึ้นถ้าแถวนั้นยังไม่มี
endolith

6
ตามลิงค์ของ endolith user.no_of_logins += 1สามารถสร้างเงื่อนไขการแข่งขันได้ ใช้แทนuser.no_of_logins = user.no_of_logins + 1. แปลเป็น SQL SET no_of_logins = no_of_logins + 1วิธีที่ถูกต้องหลังกลายเป็น:
ChaimG

5
@ChaimG ฉันเดาว่าคุณหมายถึงuser.no_of_logins = User.no_of_logins + 1หรืออีกนัยหนึ่งใช้คุณลักษณะที่เป็นเครื่องมือของโมเดลเพื่อสร้างนิพจน์ SQL เนื่องจากความคิดเห็นของคุณแสดง 2 วิธีในการสร้างสภาพการแข่งขันเดียวกัน
Ilja Everilä

2
พวกเขาจะไม่. ก่อนหน้านี้ตั้งค่าแอตทริบิวต์เป็นนิพจน์ SQL ส่วนหลังจะทำการเพิ่มแทนใน Python และแนะนำการแข่งขันอีกครั้ง
Ilja Everilä

1
@jayrizzo ฉันไม่ค่อยคุ้นเคยกับระดับการแยกธุรกรรมที่เป็นอิสระ แต่จากความเข้าใจของฉันมันจะอนุญาตให้ทำการเพิ่มใน Python โดยใช้การเพิ่มแบบแทนที่ หากมีการแข่งขันรายการใดรายการหนึ่งจะประสบความสำเร็จและธุรกรรมอื่น ๆ จะล้มเหลวและต้องลองใหม่อีกครั้ง (ด้วยสถานะใหม่ของฐานข้อมูล) แต่ฉันอาจเข้าใจผิดอย่างร้ายแรง
Ilja Everilä

343

มีหลายวิธีในการUPDATEใช้sqlalchemy

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

3
ฉันใช้setattr(query_result, key, value)สำหรับการอัปเดตบางอย่างใน Flask SQLAlchemy ตามด้วยการคอมมิต เหตุผลใดที่จะลดราคารูปแบบนี้?
Marc

2
@datamafia: คุณสามารถใช้ได้setattr(query_result, key, value)ซึ่งเทียบเท่ากับการเขียนquery_result.key = value
Nima Soroush

Thx @NimaSoroush นั่นคือสิ่งที่ฉันทำและใช่เทียบเท่า
Marc

8
เป็นไปได้หรือไม่ที่จะอธิบายความแตกต่างระหว่างกรณีเหล่านี้ ขอบคุณ!
Hatshepsut

1
@Hatshepsut ความแตกต่างอย่างหนึ่งที่ฉันเจอในวันนี้ระหว่างวันที่4ถึง1/2อยู่ที่: groups.google.com/forum/#!topic/sqlalchemy/wGUuAy27otM
Vikas Prasad

13

ตัวอย่างเพื่อชี้แจงประเด็นสำคัญในความคิดเห็นของคำตอบที่ยอมรับ

ฉันไม่เข้าใจจนกว่าฉันจะเล่นกับมันด้วยตัวเองดังนั้นฉันจึงคิดว่าจะมีคนอื่นที่สับสนเช่นกัน สมมติว่าคุณกำลังทำงานกับผู้ใช้ที่id == 6และมีno_of_logins == 30เมื่อคุณเริ่มต้น

# 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

ประเด็น

ด้วยการอ้างอิงคลาสแทนอินสแตนซ์คุณจะทำให้ SQLAlchemy ฉลาดขึ้นเกี่ยวกับการเพิ่มขึ้นโดยทำให้มันเกิดขึ้นที่ฝั่งฐานข้อมูลแทนที่จะเป็นฝั่ง Python การทำภายในฐานข้อมูลจะดีกว่าเนื่องจากไม่เสี่ยงต่อการเสียหายของข้อมูล (เช่นไคลเอนต์สองรายพยายามเพิ่มขึ้นพร้อมกันโดยให้ผลลัพธ์สุทธิเพิ่มขึ้นเพียงครั้งเดียวแทนที่จะเป็นสองรายการ) ฉันคิดว่าเป็นไปได้ที่จะทำการเพิ่มใน Python หากคุณตั้งค่าการล็อกหรือเพิ่มระดับการแยก แต่ทำไมต้องกังวลถ้าคุณไม่ต้องทำ

ข้อแม้

หากคุณกำลังจะเพิ่มขึ้นสองครั้งโดยใช้รหัสที่สร้าง SQL เช่นSET no_of_logins = no_of_logins + 1คุณจะต้องคอมมิตหรืออย่างน้อยก็ล้างระหว่างการเพิ่มขึ้นมิฉะนั้นคุณจะได้รับการเพิ่มทั้งหมดเพียงครั้งเดียว:

# 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

สวัสดีขอบคุณสำหรับคำตอบแทนที่จะเป็นตัวแปร int ฉันกำลังพยายามอัปเดตตัวแปรสตริงฉันจะทำอย่างไร ฉันใช้ฐานข้อมูล sqlite และตัวแปรที่ฉันต้องการเปลี่ยนแปลงอยู่ใน current_user ผ่านการส่งแบบฟอร์ม
dani bilel

1
@danibilel ตรวจสอบเอกสารซึ่งค่อนข้างละเอียด เป็นส่วนหนึ่งของการกวดวิชานี้ไปมากกว่าเขตสตริงปรับปรุง: docs.sqlalchemy.org/en/13/orm/... มิฉะนั้นฉันขอแนะนำให้คุณโพสต์คำถามใหม่เพื่อแสดงสิ่งที่คุณติดขัดโดยเฉพาะ
MarredCheese

ขอบคุณสำหรับลิงค์หลังจากที่ฉันใช้เวลาทั้งวันในการค้นหาฉันรู้ว่าปัญหาของฉันคือ db.session.commit () มันไม่คงอยู่ที่การเปลี่ยนแปลงในคอนโซลเท่าที่ควรฉันพบคำถามที่คล้ายกัน แต่ไม่ได้ให้คำตอบ ทำงานให้ฉันในเว็บแอปจริงยิ่งแย่ไปกว่านั้น! การเปลี่ยนแปลงไม่ได้รับการบันทึกเลยขออภัยสำหรับความคิดเห็นที่ยาว แต่ฉันไม่สามารถเพิ่มคำถามได้เนื่องจากนโยบายของเว็บไซต์ความช่วยเหลือใด ๆ จะได้รับการชื่นชม
dani bilel

4

ด้วยความช่วยเหลือของuser=User.query.filter_by(username=form.username.data).first()คำสั่งคุณจะได้รับผู้ใช้ที่ระบุในuserตัวแปร

ตอนนี้คุณสามารถเปลี่ยนค่าของตัวแปรออบเจ็กต์ใหม่เช่นuser.no_of_logins += 1และบันทึกการเปลี่ยนแปลงด้วยsessionวิธีการคอมมิต


2

ฉันเขียนโทรเลขบอทและมีปัญหากับการอัปเดตแถว ใช้ตัวอย่างนี้หากคุณมี 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')

ทำไมต้องใช้db.session.flush()? นั่นเป็นเหตุผลว่าทำไม >>> SQLAlchemy: อะไรคือความแตกต่างระหว่าง flush () และคอมมิต ()?


7
การล้างซ้ำซ้อนทั้งหมดก่อนที่จะกระทำ การกระทำมักจะล้างออกโดยปริยายเสมอ ดังที่ได้กล่าวไว้ในคำถาม / คำตอบที่คุณเชื่อมโยง: " flush()มักเรียกว่าเป็นส่วนหนึ่งของการเรียกร้องให้commit()"
Ilja Everilä
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.