Fixed #22861: Internal migrations done first so __first__ works
Thanks to Chris Beaven.
This commit is contained in:
parent
09b63a7cce
commit
2cee1d4642
|
@ -74,7 +74,7 @@ class MigrationGraph(object):
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
if not any(key[0] == node[0] for key in self.dependencies.get(node, set())) and (not app or app == node[0]):
|
if not any(key[0] == node[0] for key in self.dependencies.get(node, set())) and (not app or app == node[0]):
|
||||||
roots.add(node)
|
roots.add(node)
|
||||||
return roots
|
return sorted(roots)
|
||||||
|
|
||||||
def leaf_nodes(self, app=None):
|
def leaf_nodes(self, app=None):
|
||||||
"""
|
"""
|
||||||
|
@ -88,7 +88,7 @@ class MigrationGraph(object):
|
||||||
for node in self.nodes:
|
for node in self.nodes:
|
||||||
if not any(key[0] == node[0] for key in self.dependents.get(node, set())) and (not app or app == node[0]):
|
if not any(key[0] == node[0] for key in self.dependents.get(node, set())) and (not app or app == node[0]):
|
||||||
leaves.add(node)
|
leaves.add(node)
|
||||||
return leaves
|
return sorted(leaves)
|
||||||
|
|
||||||
def dfs(self, start, get_children):
|
def dfs(self, start, get_children):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -223,8 +223,19 @@ class MigrationLoader(object):
|
||||||
self.graph = MigrationGraph()
|
self.graph = MigrationGraph()
|
||||||
for key, migration in normal.items():
|
for key, migration in normal.items():
|
||||||
self.graph.add_node(key, migration)
|
self.graph.add_node(key, migration)
|
||||||
|
# Add all internal dependencies first to ensure __first__ dependencies
|
||||||
|
# find the correct root node.
|
||||||
for key, migration in normal.items():
|
for key, migration in normal.items():
|
||||||
for parent in migration.dependencies:
|
for parent in migration.dependencies:
|
||||||
|
if parent[0] != key[0] or parent[1] == '__first__':
|
||||||
|
# Ignore __first__ references to the same app (#22325)
|
||||||
|
continue
|
||||||
|
self.graph.add_dependency(key, parent)
|
||||||
|
for key, migration in normal.items():
|
||||||
|
for parent in migration.dependencies:
|
||||||
|
if parent[0] == key[0]:
|
||||||
|
# Internal dependencies already added.
|
||||||
|
continue
|
||||||
parent = self.check_key(parent, key[0])
|
parent = self.check_key(parent, key[0])
|
||||||
if parent is not None:
|
if parent is not None:
|
||||||
self.graph.add_dependency(key, parent)
|
self.graph.add_dependency(key, parent)
|
||||||
|
|
|
@ -51,11 +51,11 @@ class GraphTests(TestCase):
|
||||||
# Test roots and leaves
|
# Test roots and leaves
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
graph.root_nodes(),
|
graph.root_nodes(),
|
||||||
set([('app_a', '0001'), ('app_b', '0001')]),
|
[('app_a', '0001'), ('app_b', '0001')],
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
graph.leaf_nodes(),
|
graph.leaf_nodes(),
|
||||||
set([('app_a', '0004'), ('app_b', '0002')]),
|
[('app_a', '0004'), ('app_b', '0002')],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_complex_graph(self):
|
def test_complex_graph(self):
|
||||||
|
@ -105,11 +105,11 @@ class GraphTests(TestCase):
|
||||||
# Test roots and leaves
|
# Test roots and leaves
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
graph.root_nodes(),
|
graph.root_nodes(),
|
||||||
set([('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')]),
|
[('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')],
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
graph.leaf_nodes(),
|
graph.leaf_nodes(),
|
||||||
set([('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')]),
|
[('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')],
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_circular_graph(self):
|
def test_circular_graph(self):
|
||||||
|
|
|
@ -142,6 +142,26 @@ class LoaderTests(TestCase):
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@override_settings(MIGRATION_MODULES={
|
||||||
|
"migrations": "migrations.test_migrations_first",
|
||||||
|
"migrations2": "migrations2.test_migrations_2_first",
|
||||||
|
})
|
||||||
|
@modify_settings(INSTALLED_APPS={'append': 'migrations2'})
|
||||||
|
def test_first(self):
|
||||||
|
"""
|
||||||
|
Makes sure the '__first__' migrations build correctly.
|
||||||
|
"""
|
||||||
|
migration_loader = MigrationLoader(connection)
|
||||||
|
self.assertEqual(
|
||||||
|
migration_loader.graph.forwards_plan(("migrations", "second")),
|
||||||
|
[
|
||||||
|
("migrations", "thefirst"),
|
||||||
|
("migrations2", "0001_initial"),
|
||||||
|
("migrations2", "0002_second"),
|
||||||
|
("migrations", "second"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
|
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
|
||||||
def test_name_match(self):
|
def test_name_match(self):
|
||||||
"Tests prefix name matching"
|
"Tests prefix name matching"
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("migrations", "thefirst"),
|
||||||
|
("migrations2", "0002_second"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
|
||||||
|
migrations.DeleteModel("Tribble"),
|
||||||
|
|
||||||
|
migrations.RemoveField("Author", "silly_field"),
|
||||||
|
|
||||||
|
migrations.AddField("Author", "rating", models.IntegerField(default=0)),
|
||||||
|
|
||||||
|
migrations.CreateModel(
|
||||||
|
"Book",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("author", models.ForeignKey("migrations.Author", null=True)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
]
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
|
||||||
|
migrations.CreateModel(
|
||||||
|
"Author",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
("slug", models.SlugField(null=True)),
|
||||||
|
("age", models.IntegerField(default=0)),
|
||||||
|
("silly_field", models.BooleanField(default=False)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
migrations.CreateModel(
|
||||||
|
"Tribble",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("fluffy", models.BooleanField(default=True)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
]
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("migrations", "__first__"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
|
||||||
|
migrations.CreateModel(
|
||||||
|
"OtherAuthor",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
("slug", models.SlugField(null=True)),
|
||||||
|
("age", models.IntegerField(default=0)),
|
||||||
|
("silly_field", models.BooleanField(default=False)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [("migrations2", "0001_initial")]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
|
||||||
|
migrations.CreateModel(
|
||||||
|
"Bookstore",
|
||||||
|
[
|
||||||
|
("id", models.AutoField(primary_key=True)),
|
||||||
|
("name", models.CharField(max_length=255)),
|
||||||
|
("slug", models.SlugField(null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
]
|
Loading…
Reference in New Issue