Attribute history is (sometimes) not updated unless model is queried for

Issue #3857 closed
David Pärsson created an issue

Consider this example:

model = Model(string='first value')

session.add(model)
session.commit()

model.string = 'second value'

session.add(model)
session.commit()

model = session.query(Model).one()
model.string = 'third value'

session.add(model)
session.commit()

The history.deleted for this attribute is only updated after the model has been queried for:

$ python3 repro.py 
Instance is new. History for attribute "string" is: History(added=['first value'], unchanged=(), deleted=())
Instance is dirty. History for attribute "string" is: History(added=['second value'], unchanged=(), deleted=())
Instance is dirty. History for attribute "string" is: History(added=['third value'], unchanged=(), deleted=['second value'])

I would expect deleted to be ['first value'] for the first row starting with Instance is dirty..

Please se the attached file for the full, runnable example.

Comments (2)

  1. Mike Bayer repo owner

    this is by design. The attribute mechanics do not support loading of the "previous" value of a column attribute after the fact, that is, maintaining the "new" value and loading into the "committed state". There is only the option to pre-load the value up front, that is, when you actually go to set "model.string", a SQL statement is emitted to load the "old" value if not present. For a simple non-primary-key column, loading the "old" value is not necessary and would be prohibitively expensive so this is off by default. However, you can turn on active history for the column as follows:

    from sqlalchemy.orm import column_property
    
    class Model(Base):
        __tablename__ = 'my_model'
        id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
        string = column_property(
            sqlalchemy.Column(sqlalchemy.String),
            active_history=True
        )
    
  2. Log in to comment