We have a parent/child table and want to be able to propagate updates up from the children/leaves to the parent root.
For example, given we have a structure like
{
id: 1,
state: "in-progress",
children: [{
id: 2,
state: "in-progress",
children: [{
id: 3,
state: "in-progress"
}]
}]
}
When child 3
state gets updated to completed
we want to update the state of the parent 2
and its parent 1
to completed
as well. We can do that when it's one level of separation, but for two levels the root node never gets updated. So node 2
will get updated to completed
but 1
is stuck in-progress
Here's what we have
class OperationNode(BaseModel):
__tablename__ = "operation_node"
id = Column(Integer, primary_key=True)
# child -> parent
parent_id = Column(
Integer,
ForeignKey("operation_node.id", ondelete="CASCADE", onupdate="CASCADE"),
index=True,
)
# Track progress
state = Column(String(15), default=OperationNodeStates.in_progress.value)
# List of child objects with eager loading and max depth
children = relationship("OperationNode",
foreign_keys=[parent_id],
cascade="all, delete-orphan",
lazy="joined",
join_depth=2)
@event.listens_for(OperationNode, "after_update")
def after_update_trigger_parent_sync(mapper, connection, operation_node):
insp = db.inspect(operation_node)
state_history = insp.attrs["state"].load_history()
# Update aggregated fields of parent when a child has changes
if operation_node.parent_id \
and state_history.has_changes():
operation_node_table = OperationNode.__table__
connection.execute(
operation_node_table.update().
where(operation_node_table.c.id == operation_node.parent_id).
values(state="completed")
)
so when we do
operation_node = OperationNode.query.get(3)
operation_node.state = "completed"
db.session.merge(operation_node)
db.session.commit()
only the direct parent 2
gets updated from the after_update
and not the root 1
. The after_update
doesn't trigger the second time :(. Would be happy to share more code/context if needed.
Are we missing something? Or is there a good example of this same use case out there? Any help is appreciated as we are very stuck!
Using sqlalchemy 1.3.24
and Flask-SQLAlchemy
2.4.4