summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAndrew Godwin <andrew@aeracode.org>2014-01-08 13:00:12 +0000
committerAndrew Godwin <andrew@aeracode.org>2014-01-08 13:00:12 +0000
commit64887c644a8fe1004a15e27087d4e27c95c90fcd (patch)
treea0b78654bab7b23efe743a65eafb780847faac90 /django
parent0423e0796a75db8226e2032083ce6c674da337dc (diff)
Fixed #21142: Dependency failures on unmigrated apps.
Diffstat (limited to 'django')
-rw-r--r--django/db/migrations/autodetector.py1
-rw-r--r--django/db/migrations/graph.py7
-rw-r--r--django/db/migrations/loader.py35
3 files changed, 41 insertions, 2 deletions
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index b98aef2f9a..c7ac756b2e 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -299,6 +299,7 @@ class MigrationAutodetector(object):
for migration in migrations:
name_map[(app_label, migration.name)] = (app_label, "__first__")
del changes[app_label]
+ continue
# Work out the next number in the sequence
if app_leaf is None:
next_number = 1
diff --git a/django/db/migrations/graph.py b/django/db/migrations/graph.py
index 4f89fa2909..eddabbd434 100644
--- a/django/db/migrations/graph.py
+++ b/django/db/migrations/graph.py
@@ -63,14 +63,14 @@ class MigrationGraph(object):
raise ValueError("Node %r not a valid node" % (node, ))
return self.dfs(node, lambda x: self.dependents.get(x, set()))
- def root_nodes(self):
+ def root_nodes(self, app=None):
"""
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 any(key[0] == node[0] for key in self.dependencies.get(node, set())):
+ 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)
return roots
@@ -145,6 +145,9 @@ class MigrationGraph(object):
project_state = self.nodes[node].mutate_state(project_state)
return project_state
+ def __contains__(self, node):
+ return node in self.nodes
+
class CircularDependencyError(Exception):
"""
diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py
index 66d2259b79..73f9d2a71e 100644
--- a/django/db/migrations/loader.py
+++ b/django/db/migrations/loader.py
@@ -5,6 +5,9 @@ import sys
from django.apps import apps
from django.db.migrations.recorder import MigrationRecorder
from django.db.migrations.graph import MigrationGraph
+from django.db.migrations.migration import Migration
+from django.db.migrations.state import ModelState
+from django.db.migrations import operations
from django.utils import six
from django.conf import settings
@@ -191,6 +194,38 @@ class MigrationLoader(object):
self.graph.add_node(key, migration)
for key, migration in normal.items():
for parent in migration.dependencies:
+ # Special-case __first__, which means "the first migration" for
+ # migrated apps, and is ignored for unmigrated apps. It allows
+ # makemigrations to declare dependencies on apps before they
+ # even have migrations.
+ if parent[1] == "__first__" and parent not in self.graph:
+ if parent[0] in self.unmigrated_apps:
+ # This app isn't migrated, but something depends on it.
+ # We'll add a fake initial migration for it into the
+ # graph.
+ app_config = apps.get_app_config(parent[0])
+ ops = []
+ for model in app_config.get_models():
+ model_state = ModelState.from_model(model)
+ ops.append(
+ operations.CreateModel(
+ name=model_state.name,
+ fields=model_state.fields,
+ options=model_state.options,
+ bases=model_state.bases,
+ )
+ )
+ new_migration = type(
+ "FakeInitialMigration",
+ (Migration, ),
+ {"operations": ops},
+ )(parent[1], parent[0])
+ self.graph.add_node(parent, new_migration)
+ self.applied_migrations.add(parent)
+ elif parent[0] in self.migrated_apps:
+ parent = (parent[0], list(self.graph.root_nodes(parent[0]))[0])
+ else:
+ raise ValueError("Dependency on unknown app %s" % parent[0])
self.graph.add_dependency(key, parent)
def detect_conflicts(self):