如何更新SQLAlchemy行条目?

web*_*org 112 python sqlalchemy flask-sqlalchemy

假设表有三列:username,passwordno_of_logins.

当用户尝试登录时,会检查具有类似查询的条目

user = User.query.filter_by(username=form.username.data).first()
Run Code Online (Sandbox Code Playgroud)

如果密码匹配,他继续前进.我想要做的是计算用户登录的次数.因此,每当他成功登录时,我想增加该no_of_logins字段并将其存储回用户表.我不知道如何使用SqlAlchemy运行更新查询.

Nim*_*ush 297

有几种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()
Run Code Online (Sandbox Code Playgroud)

  • 是否有可能解释这些案件之间的差异?谢谢! (6认同)
  • 如果您不在 query() 中指定类对象,则第 2 点将失败。我的意思是,最终的查询是 ```session.query(User).filter(User.username == form.username.data).update({"no_of_logins": (User.no_of_logins +1)})``` (5认同)
  • 我使用 ```setattr(query_result, key, value)``` 在 Flask SQLAlchemy 中进行一些更新,然后进行提交。有什么理由打折这种模式吗? (3认同)
  • @datamafia:你可以使用`setattr(query_result,key,value)`,它完全等同于编写`query_result.key = value` (2认同)

Den*_*nis 108

user.no_of_logins += 1
session.commit()
Run Code Online (Sandbox Code Playgroud)

  • http://stackoverflow.com/a/2334917/125507说这是一个糟糕的方法.如果行还不存在怎么办? (11认同)
  • @ChaimG我猜你的意思是'user.no_of_logins = User.no_of_logins + 1`,换句话说,使用模型的检测属性来生成SQL表达式.因为它是你的评论显示2种方式来产生相同的竞争条件. (5认同)
  • 根据endolith的链接,`user.no_of_logins + = 1`可以创建竞争条件.而是使用`user.no_of_logins = user.no_of_logins + 1`.转换为sql后者正确的方式变为:`SET no_of_logins = no_of_logins + 1`. (4认同)
  • 他们不是。前者将属性设置为SQL表达式,后者在Python中执行就地加法,然后再次引入竞争。 (2认同)

Mar*_*ese 38

澄清已接受答案评论中的重要问题的示例

直到我自己玩弄它才明白它,所以我想也会有其他人感到困惑。假设您正在处理您开始时的用户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
Run Code Online (Sandbox Code Playgroud)

要点

通过引用类而不是实例,您可以让 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
Run Code Online (Sandbox Code Playgroud)


Nil*_*esh 6

user=User.query.filter_by(username=form.username.data).first()语句的帮助下,您将获得user变量中的指定用户。

现在您可以更改新对象变量的值,例如user.no_of_logins += 1使用session的 commit 方法保存更改。


小智 5

我写了电报机器人,并且在更新行方面遇到了一些问题。使用这个例子,如果你有模型

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')
Run Code Online (Sandbox Code Playgroud)

为什么使用db.session.flush()?这就是为什么 >>> SQLAlchemy:flush() 和 commit() 有什么区别?

  • 在提交之前刷新是完全多余的。提交总是隐式刷新。正如您在链接到的 Q/A 中所说:“`flush()` 总是作为调用 `commit()` 的一部分被调用” (13认同)