如何更新SQLAlchemy行条目?


136

假设表有三列:usernamepasswordno_of_logins

当用户尝试登录时,将使用查询之类的内容检查条目

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

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

Answers:


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

12
stackoverflow.com/a/2334917/125507说,这是一种不好的方法。如果该行还不存在怎么办?
endlith 2014年

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
ChaimG'7

5
@ChaimG我猜你的意思是user.no_of_logins = User.no_of_logins + 1,换句话说,使用模型的Instrumented属性生成SQL表达式。因为它是您的注释,所以显示了两种产生相同竞态条件的方法。
IljaEverilä17年

2
他们不是。前者将属性设置为SQL表达式,后者在Python中执行就地加法,然后再次引入竞争。
IljaEverilä18年

1
@jayrizzo我对SERIALIZABLE事务隔离级别不是很熟悉,但是据我所知,它将允许使用就地加法在Python中执行加法。如果发生争用,则其中一个事务将成功,而其他事务将失败,并且必须重试(使用数据库的新状态)。但是我可能误会了SERIALIZABLE。
IljaEverilä18年

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今天我遇到的41/2之间的一个区别是:groups.google.com/forum
维卡斯·普拉萨德

13

举例说明澄清已接受答案的重要问题

直到我自己玩弄它之前,我都不了解它,所以我认为还会有其他人感到困惑。假设您正在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中进行增量操作,但是如果不必这样做,为什么还要打扰呢?

注意事项

如果您要通过产生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中,通过提交一个表单
丹妮·比利尔

1
@danibilel查看文档,该文档非常详尽。本教程的这一部分将介绍更新字符串字段:docs.sqlalchemy.org/en/13/orm/…。否则,我建议您发布一个新问题,以明确表明您坚持的目标。
MarredCheese

感谢您提供的链接,在整日搜索之后,我知道我的问题出在db.session.commit()上,它没有像应该的那样持久保存控制台中的更改,我发现了类似的问题,但是提供的答案没有为我工作,在实际的Web应用程序中更糟!所做的更改根本无法保存,对冗长的评论感到抱歉,但是由于网站的政策,我无法添加任何问题,我们将不胜感激。
丹妮·比利尔

4

借助user=User.query.filter_by(username=form.username.data).first()声明,您将获得指定用户的user变量。

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


2

我写了电报bot,并且在更新行时遇到了一些问题。如果您有模型,请使用此示例

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()有什么区别?


7
刷新在提交之前是完全多余的。提交始终隐式刷新。至于多少是在Q / A说您链接到:“ flush()总是被称为呼叫的一部分commit()
ILJAEverilä
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.