Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add root_node and leaf_node functions to MigrationGraph

  • Loading branch information...
commit 8a1f0177778275d8ee4707ef0dca79553b03a035 1 parent 9ce8354
@andrewgodwin andrewgodwin authored
Showing with 46 additions and 2 deletions.
  1. +28 −2 django/db/migrations/graph.py
  2. +18 −0 tests/migrations/tests.py
View
30 django/db/migrations/graph.py
@@ -19,8 +19,9 @@ class MigrationGraph(object):
replacing migration, and repoint any dependencies that pointed to the
replaced migrations to point to the replacing one.
- A node should be a tuple: (app_path, migration_name) - but the code
- here doesn't really care.
+ A node should be a tuple: (app_path, migration_name). The tree special-cases
+ things within an app - namely, root nodes and leaf nodes ignore dependencies
+ to other apps.
"""
def __init__(self):
@@ -59,6 +60,31 @@ def backwards_plan(self, node):
raise ValueError("Node %r not a valid node" % node)
return self.dfs(node, lambda x: self.dependents.get(x, set()))
+ def root_nodes(self):
+ """
+ Returns all root nodes - that is, nodes with no dependencies inside
+ their app. These are the starting point for an app.
+ """
+ roots = set()
+ for node in self.nodes:
+ if not filter(lambda key: key[0] == node[0], self.dependencies.get(node, set())):
+ roots.add(node)
+ return roots
+
+ def leaf_nodes(self):
+ """
+ Returns all leaf nodes - that is, nodes with no dependents in their app.
+ These are the "most current" version of an app's schema.
+ Having more than one per app is technically an error, but one that
+ gets handled further up, in the interactive command - it's usually the
+ result of a VCS merge and needs some user input.
+ """
+ leaves = set()
+ for node in self.nodes:
+ if not filter(lambda key: key[0] == node[0], self.dependents.get(node, set())):
+ leaves.add(node)
+ return leaves
+
def dfs(self, start, get_children):
"""
Dynamic programming based depth first search, for finding dependencies.
View
18 tests/migrations/tests.py
@@ -44,6 +44,15 @@ def test_simple_graph(self):
graph.backwards_plan(("app_b", "0002")),
[('app_a', '0004'), ('app_a', '0003'), ('app_b', '0002')],
)
+ # Test roots and leaves
+ self.assertEqual(
+ graph.root_nodes(),
+ set([('app_a', '0001'), ('app_b', '0001')]),
+ )
+ self.assertEqual(
+ graph.leaf_nodes(),
+ set([('app_a', '0004'), ('app_b', '0002')]),
+ )
def test_complex_graph(self):
"""
@@ -81,6 +90,15 @@ def test_complex_graph(self):
graph.backwards_plan(("app_b", "0001")),
[('app_a', '0004'), ('app_c', '0002'), ('app_c', '0001'), ('app_a', '0003'), ('app_b', '0002'), ('app_b', '0001')],
)
+ # Test roots and leaves
+ self.assertEqual(
+ graph.root_nodes(),
+ set([('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')]),
+ )
+ self.assertEqual(
+ graph.leaf_nodes(),
+ set([('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')]),
+ )
def test_circular_graph(self):
"""
Please sign in to comment.
Something went wrong with that request. Please try again.