Julen
Julen

Reputation: 1144

SQLAlchemy not updating model, ignores all changed fields except the last modified one

I have the following model:

class Model(db.Base):
    """Table for storing snapshots of Projects health."""
    __tablename__ = 'my_table'

    id = Column(Integer(), primary_key=True)
    first_attribute = Column(Integer)
    second_attribute = Column(Integer)
    created_at = Column(DateTime)

I need to update an existing object's attributes. This is what it happens:

ipdb> model = (
    db.session.query(Model)
    .filter(id=5908)
    .first()
)

ipdb> logging.getLogger('sqlalchemy.engine').setLevel(logging.DEBUG)
ipdb> model.first_attribute
1
ipdb> model.second_attribute
1
ipdb> model.first_attribute = object.some_value
ipdb> model.second_attribute = object.another_value
ipdb> model.created_at = get_utc_now()
ipdb> model.first_attribute
0
ipdb> model.second_attribute
0
ipdb> db.session.add(model)
ipdb> db.session.commit()
ipdb> model.first_attribute
1
ipdb> model.second_attribute
1

So, the model originally had a value, 1, which has been changed, to 0, and after committing it, it remains 1. So is the value in database.

Actually, the debug statement admits that it doesn't care about first_attribute nor second_attribute, and it only updates created_at:

2020-02-13 10:55:29,133 - sqlalchemy.engine.base.Engine - INFO - BEGIN (implicit)
2020-02-13 10:55:29,137 - sqlalchemy.engine.base.Engine - INFO - UPDATE my_table SET created_at=%(created_at)s WHERE my_table.id = %(id)s
2020-02-13 10:55:29,138 - sqlalchemy.engine.base.Engine - INFO - {'created_at': datetime.datetime(2020, 2, 13, 9, 54, 9, 610208, tzinfo=tzutc()), 'id': 5908}
2020-02-13 10:55:29,345 - sqlalchemy.engine.base.Engine - INFO - COMMIT

The fun fact is that the only updated field is the last modified one before adding the model to the session. I've swapped model.created_at and model.first_attribute value assignments, leaving this second one as the last before db.session.add(model), and it does get updated (being this the only one updated, again).

Upvotes: 1

Views: 3704

Answers (1)

Julen
Julen

Reputation: 1144

I could make this work using db.session.merge(model) instead of db.session.add(model).

Looks like, for some reason I still don't really get, that model instance wasn't within the session scope. merge copies the state of the given instance to the one that it's within the session, based on the primary key attributes (you still need to commit the transaction to save the changes in the db).

SQLAlchemy documentation on this: https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.merge

Upvotes: 1

Related Questions