187

How can I update a row's information?

For example I'd like to alter the name column of the row that has the id 5.

Ilja Everilä
  • 50,538
  • 7
  • 126
  • 127
pocorschi
  • 3,605
  • 5
  • 26
  • 35

9 Answers9

332

Retrieve an object using the tutorial shown in the Flask-SQLAlchemy documentation. Once you have the entity that you want to change, change the entity itself. Then, db.session.commit().

For example:

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 is based on SQLAlchemy, so be sure to check out the SQLAlchemy Docs as well.

edlee
  • 665
  • 1
  • 7
  • 20
Mark Hildreth
  • 42,023
  • 11
  • 120
  • 109
  • 2
    Thanks Mark. One other thing. I've seen it done like this 'db.add(user)' then 'dv.session.commit()'. Why do the both work? and what's the difference? – pocorschi Jul 15 '11 at 07:21
  • 12
    This has to do with the differences between transient, detached, and attached objects in SQLAlchemy (see http://www.sqlalchemy.org/docs/orm/session.html#what-does-the-session-do). Also, read Michael Bayer's comment on the mailing list (http://groups.google.com/group/sqlalchemy/browse_thread/thread/e38e2aba57aeed92?pli=1) for some more info. – Mark Hildreth Jul 15 '11 at 15:28
  • 1
    If you're still confused on the differences after reading through there, consider asking another question. – Mark Hildreth Jul 15 '11 at 15:36
  • if the column data type is json, use below method. https://bashelton.com/2014/03/updating-postgresql-json-fields-via-sqlalchemy/ – Aram Jun 21 '18 at 10:14
  • @MarkHildreth I couldn't update a datetime value to the field, what should I do? column is `uesd_at = db.Column(db.DateTime)` I just run `obj.used_at = datetime.datetime.now()` `db.session.commit()` But not value set to the field. – Rukeith Oct 30 '18 at 03:51
  • @mark it give me exception that is 'dict' object has no attribute 'email' – Ferdous Wahid Dec 13 '18 at 15:57
  • it only update name, or the whole row? @MarkHildreth – Dolphin Oct 05 '20 at 05:31
122

There is a method update on BaseQuery object in SQLAlchemy, which is returned by filter_by.

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

The advantage of using update over changing the entity comes when there are many objects to be updated.

If you want to give add_user permission to all the admins,

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

Notice that filter_by takes keyword arguments (use only one =) as opposed to filter which takes an expression.

Devi
  • 5,023
  • 1
  • 34
  • 27
22

This does not work if you modify a pickled attribute of the model. Pickled attributes should be replaced in order to trigger updates:

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}
Bevan
  • 483
  • 4
  • 5
15

Just assigning the value and committing them will work for all the data types but JSON and Pickled attributes. Since pickled type is explained above I'll note down a slightly different but easy way to update JSONs.

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

Let's say the model is like above.

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

This will add the user into the MySQL database with data {"country":"Sri Lanka"}

Modifying data will be ignored. My code that didn't work is as follows.

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

Instead of going through the painful work of copying the JSON to a new dict (not assigning it to a new variable as above), which should have worked I found a simple way to do that. There is a way to flag the system that JSONs have changed.

Following is the working code.

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

This worked like a charm. There is another method proposed along with this method here Hope I've helped some one.

3

Models.py define the serializers

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

In RestApi, We can update the record dynamically by passing the json data into update query:

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
Ramesh Ponnusamy
  • 1,553
  • 11
  • 22
0

I was looking for something a little less intrusive then @Ramesh's answer (which was good) but still dynamic. Here is a solution attaching an update method to a db.Model object.

You pass in a dictionary and it will update only the columns that you pass in.

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

Then in a route you can do

object = SampleObject.query.where(SampleObject.id == id).first()
object.update(update_dictionary=request.get_json())
Moemars
  • 4,692
  • 3
  • 27
  • 30
0

Update the Columns in flask

admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email@example.com'
admin.save()
Gajanan
  • 454
  • 4
  • 11
  • 1
    Kindly add a line or two to explain what you are doing. This will help the readers to understand the answer more clearly. – Abhyuday Vaish Jun 07 '22 at 04:18
0

To use the update method (which updates the entree outside of the session) you have to query the object in steps like this:

query = db.session.query(UserModel)
query = query.filter(UserModel.id == user_id)
query.update(user_dumped)
db.session.commit()
Joe Gasewicz
  • 1,252
  • 15
  • 20
0

Solution (session) / in Flask_sqlalchemy (db):


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

db.session.commit()

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 27 '23 at 18:32