Làm cách nào để cập nhật mục hàng SQLAlchemy?


136

Giả sử bảng có ba cột: username, passwordno_of_logins.

Khi người dùng cố gắng đăng nhập, nó đã kiểm tra một mục có truy vấn như

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

Nếu mật khẩu trùng khớp, anh ta tiến hành thêm. Những gì tôi muốn làm là đếm số lần người dùng đăng nhập. Vì vậy, bất cứ khi nào anh ta đăng nhập thành công, tôi muốn tăng no_of_loginstrường và lưu nó trở lại bảng người dùng. Tôi không chắc chắn làm thế nào để chạy truy vấn cập nhật với SqlAlchemy.

Câu trả lời:


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

12
stackoverflow.com/a/2334917/125507 nói rằng đây là một cách tồi để làm điều đó. Ngoài ra, nếu hàng không tồn tại?
endolith

5
Theo liên kết của endolith, user.no_of_logins += 1có thể tạo điều kiện cuộc đua. Thay vì sử dụng user.no_of_logins = user.no_of_logins + 1. Dịch sang sql cách chính xác sau trở thành : SET no_of_logins = no_of_logins + 1.
ChaimG

5
@ChaimG Tôi đoán bạn có nghĩa là user.no_of_logins = User.no_of_logins + 1, hoặc nói cách khác, sử dụng thuộc tính cụ của mô hình để tạo ra một biểu thức SQL. Vì đó là nhận xét của bạn hiển thị 2 cách để tạo ra điều kiện cuộc đua giống nhau.
Ilja Everilä

2
Họ không phải. Cái trước đặt thuộc tính cho một biểu thức SQL, cái sau thực hiện bổ sung tại chỗ trong Python và một lần nữa giới thiệu cuộc đua.
Ilja Everilä

1
@jayrizzo Tôi không quen thuộc với mức cô lập giao dịch SERIALIZABLE, nhưng theo hiểu biết của tôi, nó sẽ cho phép thực hiện bổ sung trong Python bằng cách sử dụng bổ sung tại chỗ. Nếu có một cuộc đua, một trong những giao dịch sau đó sẽ thành công và những giao dịch khác sẽ thất bại và phải thử lại (với trạng thái mới của DB). Nhưng tôi có thể đã hiểu nhầm SERIALIZABLE.
Ilja Everilä

342

Có một số cách để UPDATEsử dụngsqlalchemy

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
Tôi sử dụng setattr(query_result, key, value)cho một số cập nhật trong Flask SQLAlchemy theo sau là một cam kết. Bất kỳ lý do để giảm giá mô hình này?
Marc

2
@datamafia: Bạn có thể sử dụng setattr(query_result, key, value), chính xác tương đương với văn bảnquery_result.key = value
Nima Soroush

Thx @NimaSioussh, đó là những gì tôi làm và có tương đương.
Marc

8
Có thể giải thích sự khác biệt giữa các trường hợp này? Cảm ơn!
Hatshepsut

1
@Hatshepsut Một sự khác biệt mà tôi đã gặp hôm nay giữa 41/2 là tại: Groups.google.com/forum/#!topic/sqlalchemy/wGUuAy27otM
Vikas Prasad

13

Các ví dụ để làm rõ vấn đề quan trọng trong các bình luận của câu trả lời được chấp nhận

Tôi đã không hiểu nó cho đến khi tôi tự chơi xung quanh nó, vì vậy tôi đoán rằng sẽ có những người khác cũng bối rối. Giả sử bạn đang làm việc với người dùng khi nào id == 6no_of_logins == 30khi nào bạn bắt đầu.

# 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

Điểm

Bằng cách tham chiếu lớp thay vì thể hiện, bạn có thể khiến SQLAlchemy trở nên thông minh hơn về việc tăng, làm cho nó xảy ra ở phía cơ sở dữ liệu thay vì phía Python. Làm điều đó trong cơ sở dữ liệu sẽ tốt hơn vì nó ít dễ bị hỏng dữ liệu hơn (ví dụ: hai khách hàng cố gắng tăng cùng một lúc với kết quả ròng chỉ là một lần tăng thay vì hai lần). Tôi cho rằng có thể thực hiện tăng dần trong Python nếu bạn đặt khóa hoặc tăng mức cô lập, nhưng tại sao phải bận tâm nếu bạn không phải làm vậy?

Một cảnh báo

Nếu bạn sẽ tăng hai lần thông qua mã tạo ra SQL như thế nào SET no_of_logins = no_of_logins + 1, thì bạn sẽ cần phải cam kết hoặc ít nhất là xóa giữa các mức tăng, nếu không, bạn sẽ chỉ nhận được tổng số gia tăng:

# 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

Xin chào, cảm ơn câu trả lời của bạn, thay vì một biến int tôi đang cố gắng cập nhật một biến chuỗi, làm thế nào để tôi làm điều đó? Tôi đang sử dụng cơ sở dữ liệu sqlite và các biến tôi muốn thay đổi nằm trong current_user, thông qua việc gửi biểu mẫu
dani bilel

1
@danibilel Kiểm tra tài liệu, khá kỹ lưỡng. Phần này của hướng dẫn đi qua việc cập nhật các trường chuỗi: docs.sqlalchemy.org/en/13/orm/ mẹo . Mặt khác, tôi đề nghị bạn đăng một câu hỏi mới cho thấy những gì bạn đang mắc kẹt cụ thể.
MarredCheese

Cảm ơn về liên kết, sau khi tôi dành cả ngày để tìm kiếm, tôi biết vấn đề của mình là do db.session.commit (), nó không tiếp tục thay đổi trong bảng điều khiển như tôi nên, tôi đã tìm thấy câu hỏi tương tự nhưng không cung cấp câu trả lời làm việc cho tôi, trong ứng dụng web thực tế, nó thậm chí còn tồi tệ hơn! các thay đổi hoàn toàn không được lưu, xin lỗi vì nhận xét dài nhưng tôi không thể thêm câu hỏi vì chính sách của trang web, mọi trợ giúp đều được đánh giá cao.
dani bilel

4

Với sự trợ giúp của user=User.query.filter_by(username=form.username.data).first()câu lệnh, bạn sẽ có được người dùng được chỉ định trong userbiến.

Bây giờ bạn có thể thay đổi giá trị của biến đối tượng mới như user.no_of_logins += 1và lưu các thay đổi bằng sessionphương thức cam kết.


2

Tôi đã viết bot telegram, và có một số vấn đề với các hàng cập nhật. Sử dụng ví dụ này, nếu bạn có 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')

Tại sao nên sử dụng db.session.flush()? Đó là lý do tại sao >>> SQLAlchemy: Sự khác biệt giữa flush () và commit () là gì?


7
Việc xả nước là hoàn toàn dư thừa ngay trước khi cam kết. Một cam kết luôn ngầm ngầm. Như đã nói trong phần Hỏi / Đáp mà bạn liên kết đến: " flush()luôn được gọi là một phần của cuộc gọi đến commit()"
Ilja Everilä
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.