【问题标题】:How to update SQLAlchemy row entry?如何更新 SQLAlchemy 行条目?
【发布时间】:2012-03-28 20:33:32
【问题描述】:

假设表有三列:usernamepasswordno_of_logins

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

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

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

【问题讨论】:

    标签: python sqlalchemy flask-sqlalchemy


    【解决方案1】:

    使用sqlalchemyUPDATE 有几种方法

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

    【讨论】:

    • 我在 Flask SQLAlchemy 中使用setattr(query_result, key, value) 进行一些更新,然后提交。有什么理由打折这种模式?
    • @datamafia:可以使用setattr(query_result, key, value),完全等价于写query_result.key = value
    • 能否解释这些案例之间的差异?谢谢!
    • @Hatshepsut 我今天在 41/2 之间遇到的一个区别是:groups.google.com/forum/#!topic/sqlalchemy/wGUuAy27otM
    • 如果您不在 query() 中指定类对象,第 2 点将失败。我的意思是,最终查询是session.query(User).filter(User.username == form.username.data).update({"no_of_logins": (User.no_of_logins +1)})
    【解决方案2】:
    user.no_of_logins += 1
    session.commit()
    

    【讨论】:

    • stackoverflow.com/a/2334917/125507 说这是一种不好的做法。如果该行还不存在怎么办?
    • 根据 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 我猜你的意思是user.no_of_logins = User.no_of_logins + 1,或者换句话说,使用模型的检测属性来生成 SQL 表达式。因为您的评论显示了产生相同竞争条件的 2 种方法。
    • 他们不是。前者将属性设置为 SQL 表达式,后者在 Python 中执行就地加法并再次引入竞争。
    • @jayrizzo 我对 SERIALIZABLE 事务隔离级别不太熟悉,但据我了解,它允许使用就地添加在 Python 中执行添加。如果存在竞争,则其中一个事务将成功,其他事务将失败并且必须重试(使用数据库的新状态)。但我可能误解了 SERIALIZABLE。
    【解决方案3】:

    在已接受答案的 cmets 中阐明重要问题的示例

    直到我自己玩弄它时我才明白它,所以我想会有其他人也感到困惑。假设您在开始时正在处理id == 6no_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 中进行递增,但如果您不必这样做,为什么还要麻烦呢?

    警告

    如果您要通过生成像SET no_of_logins = no_of_logins + 1 这样的 SQL 的代码来增加两次,那么您将需要提交或至少在两次增量之间刷新,否则您总共只会得到一个增量:

    # 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 中,通过提交表单
    • @danibilel 查看文档,该文档非常详尽。本教程的这一部分将讨论更新字符串字段:docs.sqlalchemy.org/en/13/orm/…。否则,我建议您发布一个新问题,说明您具体遇到了什么问题。
    • 感谢链接,在我花了一整天的时间搜索之后,我知道我的问题出在 db.session.commit() 上,它没有像应有的那样在控制台中保留更改,我发现类似的问题,但提供的答案对我不起作用,在实际的网络应用程序中更糟!更改根本没有保存,很抱歉长评论,但由于网站的政策,我无法添加问题,任何帮助将不胜感激。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案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')
      

      为什么使用db.session.flush()?这就是为什么 >>> SQLAlchemy: What's the difference between flush() and commit()?

      【讨论】:

      • 在提交之前刷新完全是多余的。提交总是隐式刷新。正如您链接到的 Q/A 中所说的那样:“flush() 始终作为对 commit() 的调用的一部分被调用”
      猜你喜欢
      • 2021-03-07
      • 1970-01-01
      • 2013-10-10
      • 2021-11-03
      • 2012-07-02
      • 2019-11-08
      • 2014-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多