59

We have a model with a JSON field where user flags get inserted. Inserting does work as expected, but when removing certain flags, they stay in the field and changes don't get persisted to the DB.

We have the following method in our model:

def del_flag(self, key):
    if self.user_flags is None or not key in self.user_flags:
        return False
    else:
        del self.user_flags[key]
        db.session.commit()        
        return True

The databasse is postgres and we use the SQLalchemy JSON field dialect for the field type. Any advice on this?

Ilja Everilä
  • 50,538
  • 7
  • 126
  • 127
trnc
  • 20,581
  • 21
  • 60
  • 98

4 Answers4

95

If you are using Postgres < 9.4 you can't update JSON field directly. You need flag_modified function to report the change to SQLAlchemy:

from sqlalchemy.orm.attributes import flag_modified
model.data['key'] = 'New value'
flag_modified(model, "data")
session.add(model)
session.commit()
José Vte. Calderón
  • 1,348
  • 12
  • 17
  • 13
    I am using Postgres 10.3 and SQLAlchemy 1.2.8 with a column that looks like `data = Column(MutableDict.as_mutable(JSON))` and I still had to do this to make an update work. – Bob Jordan Aug 07 '18 at 14:24
  • 2
    Thank you @josé-vte-calderón. Took me quite a while to figure out that SQLAlchemy was not registering changes made to JSON fields. Thanks! – dataflow Oct 17 '18 at 16:48
  • for those who don't scroll too much down, please see the latest response from @humbledude – Michał Schielmann Jun 13 '19 at 09:52
  • 2
    There is no need to add the model object to the DB unless it is new. If we are modifying an existing object then session.commit() will save the changes down. – Jake Jul 28 '22 at 18:05
  • Spent lot of time to solve this problem, but this is the place I found solution at – Yash Pokar Jan 18 '23 at 10:14
35

I'm using JSON field and I referred below document.

https://docs.sqlalchemy.org/en/13/core/type_basics.html?highlight=json#sqlalchemy.types.JSON

It shows how to make JSON-dict field mutable. (Default is immutable)

like this..

from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy import Column, Integer, JSON

class TableABC(Base):
    __tablename__ = 'table_abc'
    id = Column(Integer, primary_key=True)
    info = Column(MutableDict.as_mutable(JSON))

Then I could update json field as ORM.

humbledude
  • 479
  • 4
  • 6
  • 2
    SQLAlchemy==1.1.18 and PostgreSQL 9.6 works as expected with `JSONB` type – El Ruso Jun 18 '19 at 09:28
  • 2
    Hi @humbledude. This method did not work for me, using sqlalchemy version 1.3.6. I'd recommend the solution using flag_modified as somebody else suggested. It works well – Skulas Jul 25 '19 at 14:24
  • 1
    Hi @Skulas. this code seems to work depending on version of DB. I am using Postgresql(Enterprise) 10.1.5 and MySql 5.7.17. I couldn't find version limitation info on official SQLAlchemy doc. – humbledude Aug 06 '19 at 02:07
34

My issue was referencing the row object returned from SQLAlchemy when creating the new row. e.g. this does not work:

row = db.session.query(SomeTable).filter_by(id=someId).first()
print(row.details)
newDetails = row.details
newDetails['key'] = 'new data'
row.details = newDetails
db.session.commit()

but creating a new dict does work

row = db.session.query(SomeTable).filter_by(id=someId).first()
print(row.details)
newDetails = dict(row.details)
newDetails['key'] = 'new data'
row.details = newDetails
db.session.commit()

notice dict(row.details)

0

What worked for me is this-

user = db.query(User).filter(User.id==id).first()

#change user to dictionary and make required changes in the object and create modified_user_dict

db.query(User).filter(User.id==modified_user_dict['id']).update(values=modified_user_dict)
db.commit()
moken
  • 3,227
  • 8
  • 13
  • 23
  • Answer needs supporting information Your answer could be improved with additional supporting information. Please [edit](https://stackoverflow.com/posts/76273344/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](https://stackoverflow.com/help/how-to-answer). – moken May 21 '23 at 06:25