edelooff / sqlalchemy-json

Full-featured JSON type with mutation tracking for SQLAlchemy
http://variable-scope.com/posts/mutation-tracking-in-nested-json-structures-using-sqlalchemy
BSD 2-Clause "Simplified" License
189 stars 34 forks source link

usecase: how to get the object history data to track obj change in event? #49

Open wangxin688 opened 1 year ago

wangxin688 commented 1 year ago

I try to get object change using following code, but MutableJSON has no history getted

def get_object_changes(obj):
    """Given a model instance, returns dict of pending
    changes waiting for database flush/commit.

    e.g. {
        "pre_change": {},
        "post_change": {},
        "diff": {}
    }
    """
    inspection = inspect(obj)
    changes = {
        "pre_change": {},
        "post_change": {},
        "diff": {},
    }
    for attr in class_mapper(obj.__class__).column_attrs:
        if getattr(inspection.attrs, attr.key).history.has_changes():
            if get_history(obj, attr.key)[2]:
                before = get_history(obj, attr.key)[2].pop()
                after = getattr(obj, attr.key)
                changes["pre_change"][attr.key] = before
                changes["post_change"][attr.key] = after
                if before != after:
                    if before or after:
                        changes["diff"][attr.key] = {"before": before, "after": after}
    return jsonable_encoder(changes)
edelooff commented 1 year ago

I don't know the exact behaviour of get_history, assuming this is a SQLAlchemy function, but assuming it gets the previous object from the session, what you are showing is expected behaviour.

The object gets modified in-place, meaning that the reference to the "previous" value refers to the same object as the "current", and there is no difference between them.

Making changes like this detectable would require some level of explicit (deep) copying of the original data, which would be very unexpected.

You might be able to subclass the MutableJSON type to enable something like that, but it's not something suitable for the basic behaviour I think.

wangxin688 commented 1 year ago

I don't know the exact behaviour of get_history, assuming this is a SQLAlchemy function, but assuming it gets the previous object from the session, what you are showing is expected behaviour.

The object gets modified in-place, meaning that the reference to the "previous" value refers to the same object as the "current", and there is no difference between them.

Making changes like this detectable would require some level of explicit (deep) copying of the original data, which would be very unexpected.

You might be able to subclass the MutableJSON type to enable something like that, but it's not something suitable for the basic behaviour I think.

sqlalchemy.orm.attributes import get_history will return History object which has three attr: added : the collection of elements added to the attribute

unchanged : the collection of elements that have not changed on the attribute

deleted : the collection of elements that have been deleted from the attribute. I guess you are right, i might need to use subclass to implement this