Flask-SQLalchemy更新行信息

188

如何更新一行的信息?

例如,我想修改id为5的行的名称列。

9个回答

332

使用Flask-SQLAlchemy文档中所示的教程检索对象。一旦获得要更改的实体,请更改实体本身。然后,db.session.commit()

例如:

admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email@example.com'
db.session.commit()

user = User.query.get(5)
user.name = 'New Name'
db.session.commit()

Flask-SQLAlchemy基于SQLAlchemy,因此请务必查看SQLAlchemy文档


2
谢谢Mark。还有一件事。我看到过这样的写法 'db.add(user)' 然后是 'dv.session.commit()'。为什么两种方式都可以?它们之间有什么区别吗? - pocorschi
12
这与SQLAlchemy中瞬态、游离和关联对象之间的区别有关(请参见http://www.sqlalchemy.org/docs/orm/session.html#what-does-the-session-do)。此外,请阅读迈克尔·拜尔在邮件列表中的评论(http://groups.google.com/group/sqlalchemy/browse_thread/thread/e38e2aba57aeed92?pli=1)以获取更多信息。 - Mark Hildreth
1
如果你在阅读后仍对区别感到困惑,考虑提出另一个问题。 - Mark Hildreth
如果列数据类型为json,请使用以下方法。https://bashelton.com/2014/03/updating-postgresql-json-fields-via-sqlalchemy/ - Aram
@MarkHildreth 我无法将datetime值更新到字段中,我该怎么办? 列为 uesd_at = db.Column(db.DateTime) 我刚才运行了 obj.used_at = datetime.datetime.now() db.session.commit() 但是没有将值设置到字段中。 - Rukeith
显示剩余2条评论

125

在SQLAlchemy中,filter_by返回一个BaseQuery对象,该对象有一个方法update

num_rows_updated = User.query.filter_by(username='admin').update(dict(email='my_new_email@example.com')))
db.session.commit()

使用 update 来更新实体的优势在于需要更新许多对象时。

如果你想将 add_user 权限赋予所有的 admin

rows_changed = User.query.filter_by(role='admin').update(dict(permission='add_user'))
db.session.commit()

请注意filter_by使用关键字参数(仅使用一个=),而filter则采用表达式。


1
在第一个查询中,结果被命名为“admin”,这可能会误导,因为结果将是更新的行数。不是吗? - Vikas Prasad
有没有一种方法可以获取查询所影响的“用户”项,而不是受影响用户的数量? - Vikas Prasad
匹配的行数 - Vikas Prasad
为什么你需要将一个名为'admin'的变量传递给更新函数?它在任何地方都没有被使用。 - d1spstack

22

如果你修改了模型的一个已pickle化的属性,这种方法就不起作用了。应该替换pickle化的属性以引发更新:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from pprint import pprint

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqllite:////tmp/users.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    data = db.Column(db.PickleType())

    def __init__(self, name, data):
        self.name = name
        self.data = data

    def __repr__(self):
        return '<User %r>' % self.username

db.create_all()

# Create a user.
bob = User('Bob', {})
db.session.add(bob)
db.session.commit()

# Retrieve the row by its name.
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {}

# Modifying data is ignored.
bob.data['foo'] = 123
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {}

# Replacing data is respected.
bob.data = {'bar': 321}
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {'bar': 321}

# Modifying data is ignored.
bob.data['moo'] = 789
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {'bar': 321}

1
在这种情况下,最好的方法是什么? - kampta
你需要复制 data 并重新分配它。 - sas
@sas 你是什么意思? - Mote Zart
您需要复制并分配给数据属性以触发更新机制。请查看下面的答案:user.data = data - sas
为什么在你的示例中修改数据被忽略了?@Bevan - acebelowzero

15

只是分配值并提交它们将适用于所有数据类型,但不适用于JSON和Pickled属性。由于上面已经解释了pickled类型,我会记录一种略有不同但容易更新JSON的方法。

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    data = db.Column(db.JSON)

def __init__(self, name, data):
    self.name = name
    self.data = data

假设模型如上所示。

user = User("Jon Dove", {"country":"Sri Lanka"})
db.session.add(user)
db.session.flush()
db.session.commit()

这将把用户添加到MySQL数据库中,数据为{"country":"斯里兰卡"}

修改数据将被忽略。我的代码如下所示,但未能正常工作。

user = User.query().filter(User.name=='Jon Dove')
data = user.data
data["province"] = "south"
user.data = data
db.session.merge(user)
db.session.flush()
db.session.commit()

不必像上面将JSON复制到一个新的字典中(也不要将其分配给一个新变量),这样做是很痛苦的。我找到了一种简单的方法来解决这个问题。有一种方法可以向系统标记JSON已更改。

以下是可行的代码。

from sqlalchemy.orm.attributes import flag_modified
user = User.query().filter(User.name=='Jon Dove')
data = user.data
data["province"] = "south"
user.data = data
flag_modified(user, "data")
db.session.merge(user)
db.session.flush()
db.session.commit()

这个方法非常好用。除了这种方法外,还有另一种方法在这里提出here。希望我能帮助到您。


4
db.session.merge(user) 加入这段代码对我很有帮助,供您参考。 - Jeff Bluemel
谢谢,这正是我在寻找的。 - Atif Shafi

3

models.py 定义序列化程序

def default(o):
   if isinstance(o, (date, datetime)):
      return o.isoformat()

def get_model_columns(instance,exclude=[]):
    columns=instance.__table__.columns.keys()
    columns=list(set(columns)-set(exclude))
    return columns

class User(db.Model):
   __tablename__='user'
   id = db.Column(db.Integer, primary_key=True, autoincrement=True)
   .......
   ####

    def serializers(self):
       cols = get_model_columns(self)
       dict_val = {}
       for c in cols:
           dict_val[c] = getattr(self, c)
       return json.loads(json.dumps(dict_val,default=default))

在RestApi中,我们可以通过将JSON数据传递到更新查询中来动态更新记录:
class UpdateUserDetails(Resource):
   @auth_token_required
   def post(self):
      json_data = request.get_json()
      user_id = current_user.id
      try:
         instance = User.query.filter(User.id==user_id)
         data=instance.update(dict(json_data))
         db.session.commit()
         updateddata=instance.first()
         msg={"msg":"User details updated successfully","data":updateddata.serializers()}
         code=200
      except Exception as e:
         print(e)
         msg = {"msg": "Failed to update the userdetails! please contact your administartor."}
         code=500
      return msg

its short and best! - ANONYMUS
1
我使用你的实例方法 @Ramesh Ponnusamy 更新了长字典数据,并在此过程中学到了很多。谢谢。 - dannisis

0
要使用update方法(更新会话外的条目),您必须按照以下步骤查询对象:
query = db.session.query(UserModel)
query = query.filter(UserModel.id == user_id)
query.update(user_dumped)
db.session.commit()

0
我正在寻找比@Ramesh的答案(很好)稍微不那么侵入性但仍然具有动态性的东西。这里是一种解决方案,将更新方法附加到db.Model对象上。
您传递一个字典,它将只更新您传递的列。
class SampleObject(db.Model):
  id = db.Column(db.BigInteger, primary_key=True)
  name = db.Column(db.String(128), nullable=False)
  notes = db.Column(db.Text, nullable=False)

  def update(self, update_dictionary: dict):
    for col_name in self.__table__.columns.keys():
      if col_name in update_dictionary:
        setattr(self, col_name, update_dictionary[col_name])

    db.session.add(self)
    db.session.commit()

然后在路由中你可以这样做

object = SampleObject.query.where(SampleObject.id == id).first()
object.update(update_dictionary=request.get_json())

0

在 Flask 中更新列

admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email@example.com'
admin.save()

1
请添加一两行说明你正在做什么。这将有助于读者更清楚地理解答案。 - Abhyuday Vaish

0

解决方案(会话)/ 在Flask_sqlalchemy(数据库)中:


db.session.query(BlogPost).where(BlogPost.id == post_id).update({ "attribute01": new_data, "attribute02": new_data })

db.session.commit()


你的回答可以通过提供额外的支持信息来改进。请[编辑]以添加更多细节,例如引用或文档,以便他人可以确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接