Source code for examples.adjacency_list.adjacency_list

  1. from sqlalchemy import Column
  2. from sqlalchemy import create_engine
  3. from sqlalchemy import ForeignKey
  4. from sqlalchemy import Integer
  5. from sqlalchemy import String
  6. from sqlalchemy.ext.declarative import declarative_base
  7. from sqlalchemy.orm import backref
  8. from sqlalchemy.orm import joinedload
  9. from sqlalchemy.orm import relationship
  10. from sqlalchemy.orm import Session
  11. from sqlalchemy.orm.collections import attribute_mapped_collection
  12. Base = declarative_base()
  13. class TreeNode(Base):
  14. __tablename__ = "tree"
  15. id = Column(Integer, primary_key=True)
  16. parent_id = Column(Integer, ForeignKey(id))
  17. name = Column(String(50), nullable=False)
  18. children = relationship(
  19. "TreeNode",
  20. # cascade deletions
  21. cascade="all, delete-orphan",
  22. # many to one + adjacency list - remote_side
  23. # is required to reference the 'remote'
  24. # column in the join condition.
  25. backref=backref("parent", remote_side=id),
  26. # children will be represented as a dictionary
  27. # on the "name" attribute.
  28. collection_class=attribute_mapped_collection("name"),
  29. )
  30. def __init__(self, name, parent=None):
  31. self.name = name
  32. self.parent = parent
  33. def __repr__(self):
  34. return "TreeNode(name=%r, id=%r, parent_id=%r)" % (
  35. self.name,
  36. self.id,
  37. self.parent_id,
  38. )
  39. def dump(self, _indent=0):
  40. return (
  41. " " * _indent
  42. + repr(self)
  43. + "\n"
  44. + "".join([c.dump(_indent + 1) for c in self.children.values()])
  45. )
  46. if __name__ == "__main__":
  47. engine = create_engine("sqlite://", echo=True)
  48. def msg(msg, *args):
  49. msg = msg % args
  50. print("\n\n\n" + "-" * len(msg.split("\n")[0]))
  51. print(msg)
  52. print("-" * len(msg.split("\n")[0]))
  53. msg("Creating Tree Table:")
  54. Base.metadata.create_all(engine)
  55. session = Session(engine)
  56. node = TreeNode("rootnode")
  57. TreeNode("node1", parent=node)
  58. TreeNode("node3", parent=node)
  59. node2 = TreeNode("node2")
  60. TreeNode("subnode1", parent=node2)
  61. node.children["node2"] = node2
  62. TreeNode("subnode2", parent=node.children["node2"])
  63. msg("Created new tree structure:\n%s", node.dump())
  64. msg("flush + commit:")
  65. session.add(node)
  66. session.commit()
  67. msg("Tree After Save:\n %s", node.dump())
  68. TreeNode("node4", parent=node)
  69. TreeNode("subnode3", parent=node.children["node4"])
  70. TreeNode("subnode4", parent=node.children["node4"])
  71. TreeNode("subsubnode1", parent=node.children["node4"].children["subnode3"])
  72. # remove node1 from the parent, which will trigger a delete
  73. # via the delete-orphan cascade.
  74. del node.children["node1"]
  75. msg("Removed node1. flush + commit:")
  76. session.commit()
  77. msg("Tree after save:\n %s", node.dump())
  78. msg(
  79. "Emptying out the session entirely, selecting tree on root, using "
  80. "eager loading to join four levels deep."
  81. )
  82. session.expunge_all()
  83. node = (
  84. session.query(TreeNode)
  85. .options(
  86. joinedload("children")
  87. .joinedload("children")
  88. .joinedload("children")
  89. .joinedload("children")
  90. )
  91. .filter(TreeNode.name == "rootnode")
  92. .first()
  93. )
  94. msg("Full Tree:\n%s", node.dump())
  95. msg("Marking root node as deleted, flush + commit:")
  96. session.delete(node)
  97. session.commit()