| 64 | |
| 65 | |
| 66 | class MigrationGraph: |
| 67 | def __init__(self) -> None: |
| 68 | self.node_map: dict[MigrationKey, Node] = {} |
| 69 | self.nodes: dict[MigrationKey, Migration | None] = {} |
| 70 | |
| 71 | def add_node(self, key: MigrationKey, migration: Migration) -> None: |
| 72 | if key in self.node_map: |
| 73 | raise ValueError(f"Duplicate migration node {key}") |
| 74 | node = Node(key) |
| 75 | self.node_map[key] = node |
| 76 | self.nodes[key] = migration |
| 77 | |
| 78 | def add_dummy_node(self, key: MigrationKey, origin: MigrationKey, error_message: str) -> None: |
| 79 | node = DummyNode(key, origin, error_message) |
| 80 | self.node_map[key] = node |
| 81 | self.nodes[key] = None |
| 82 | |
| 83 | def add_dependency( |
| 84 | self, |
| 85 | migration: MigrationKey, |
| 86 | child: MigrationKey, |
| 87 | parent: MigrationKey, |
| 88 | *, |
| 89 | skip_validation: bool = False, |
| 90 | ) -> None: |
| 91 | if child not in self.nodes: |
| 92 | self.add_dummy_node( |
| 93 | child, |
| 94 | migration, |
| 95 | f"Migration {migration} references nonexistent child {child}", |
| 96 | ) |
| 97 | if parent not in self.nodes: |
| 98 | self.add_dummy_node( |
| 99 | parent, |
| 100 | migration, |
| 101 | f"Migration {migration} references nonexistent parent {parent}", |
| 102 | ) |
| 103 | self.node_map[child].add_parent(self.node_map[parent]) |
| 104 | self.node_map[parent].add_child(self.node_map[child]) |
| 105 | if not skip_validation: |
| 106 | self.validate_consistency() |
| 107 | |
| 108 | def validate_consistency(self) -> None: |
| 109 | for node in self.node_map.values(): |
| 110 | if isinstance(node, DummyNode): |
| 111 | node.raise_error() |
| 112 | |
| 113 | def root_nodes(self, app_label: str | None = None) -> list[MigrationKey]: |
| 114 | nodes = [ |
| 115 | node.key |
| 116 | for node in self.node_map.values() |
| 117 | if not node.parents and (app_label is None or node.key.app_label == app_label) |
| 118 | ] |
| 119 | return sorted(nodes) |
| 120 | |
| 121 | def leaf_nodes(self, app_label: str | None = None) -> list[MigrationKey]: |
| 122 | nodes = [ |
| 123 | node.key |
no outgoing calls
searching dependent graphs…