web-dev-qa-db-ja.com

SQLAlchemyの行エントリを更新する方法は?

テーブルには、usernamepassword、およびno_of_loginsの3つの列があるとします。

ユーザーがログインしようとすると、次のようなクエリでエントリがチェックされます

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

パスワードが一致する場合、彼はさらに先へ進みます。私がやりたいのは、ユーザーが何回ログインしたかを数えることです。したがって、彼が正常にログインするたびに、no_of_loginsフィールドをインクリメントし、ユーザーテーブルに戻します。 SqlAlchemyで更新クエリを実行する方法がわかりません。

107
webminal.org
user.no_of_logins += 1
session.commit()
103
Denis

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()
283
Nima Soroush

user=User.query.filter_by(username=form.username.data).first()ステートメントを使用すると、user変数で指定されたユーザーを取得できます。

これで、user.no_of_logins += 1のような新しいオブジェクト変数の値を変更し、sessionのc​​ommitメソッドで変更を保存できます。

3
Nilesh

受け入れられた回答のコメントで重要な問題を明確にする例

自分で試してみるまで理解できなかったので、混乱する人もいると思いました。開始時に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側ではなくデータベース側で発生するようになります。データベース内で実行すると、データ破損に対する脆弱性が低くなるため(たとえば、2つのクライアントが同時に増分しようとすると、最終的に2つではなく1つしか増分されません)、データベース内で行う方が適切です。ロックを設定したり、分離レベルを上げたりすれば、Pythonでインクリメントすることが可能だと思いますが、なぜそうする必要がないのでしょうか?

警告

SET no_of_logins = no_of_logins + 1のようなSQLを生成するコードを介して2回インクリメントする場合は、コミットするか、少なくともインクリメント間でフラッシュする必要があります。

# 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
0
MarredCheese

私は電報ボットを作成しましたが、更新行に問題があります。モデルがある場合は、この例を使用してください

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()とcommit()の違いは何ですか?

0
Andrew Lutsyuk