Mutation tracking in nested JSON structures using SQLAlchemy
This is part two of a two-part post on storage of JSON using SQLAlchemy. The first post covered the basics of creating a JSON column type and tracking mutations. In this post, we will continue from there to cover mutation tracking in arbitrarily nested structures.
In the previous post we ended with an example of appending to an existing list. Upon committing the changes in the session and reloading the object, it was shown the appended string had not been stored. This happened because changing the list in-place did not trigger the changed()
method of the class MutableDict
. Only setting or deleting a key from the dictionary marks it as changed, and marking it as changed upon access (which is all we did on the dictionary itself) would cause far too many updates of the database.
What we wanted (and perhaps expected) is behavior where changing the list marks the dictionary it’s part of as changed. And for completeness, if the dictionary contained a number of nested dictionaries, changing any of them at any level should mark the class MutableDict
as changed. To achieve this, we need a solution that consists of the following parts:
- Replacement types for
list
anddict
where all methods that change the object in-place flag it as having changed. - A means to propagate the notification of change up to the top so that it reaches the
class MutableDict
. - Conversion of all mutable types to the defined replacement types. Both when they are added to the existing structure, as well as on load from the database.