From 5598705b30a9dba008cd36018dcbccca48f7899b Mon Sep 17 00:00:00 2001
From: Steadman <mark@steadman.io>
Date: Thu, 4 Jun 2015 14:47:44 +0100
Subject: [PATCH] Fixes #24899

---
 django/db/migrations/autodetector.py | 127 ++++++++++++++++++++---------------
 1 file changed, 71 insertions(+), 56 deletions(-)

diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index 9a4ba42..bf23980 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -155,35 +155,9 @@ class MigrationAutodetector(object):
         # Renames have to come first
         self.generate_renamed_models()
 
-        # Prepare field lists, and prepare a list of the fields that used
-        # through models in the old state so we can make dependencies
-        # from the through model deletion to the field that uses it.
-        self.kept_model_keys = set(self.old_model_keys).intersection(self.new_model_keys)
-        self.kept_proxy_keys = set(self.old_proxy_keys).intersection(self.new_proxy_keys)
-        self.kept_unmanaged_keys = set(self.old_unmanaged_keys).intersection(self.new_unmanaged_keys)
-        self.through_users = {}
-        self.old_field_keys = set()
-        self.new_field_keys = set()
-        for app_label, model_name in sorted(self.kept_model_keys):
-            old_model_name = self.renamed_models.get((app_label, model_name), model_name)
-            old_model_state = self.from_state.models[app_label, old_model_name]
-            new_model_state = self.to_state.models[app_label, model_name]
-            self.old_field_keys.update((app_label, model_name, x) for x, y in old_model_state.fields)
-            self.new_field_keys.update((app_label, model_name, x) for x, y in new_model_state.fields)
-
-        # Through model map generation
-        for app_label, model_name in sorted(self.old_model_keys):
-            old_model_name = self.renamed_models.get((app_label, model_name), model_name)
-            old_model_state = self.from_state.models[app_label, old_model_name]
-            for field_name, field in old_model_state.fields:
-                old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name)
-                if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None)
-                        and not old_field.remote_field.through._meta.auto_created):
-                    through_key = (
-                        old_field.remote_field.through._meta.app_label,
-                        old_field.remote_field.through._meta.model_name,
-                    )
-                    self.through_users[through_key] = (app_label, old_model_name, field_name)
+        # Prepare lists of fields and generate through model map
+        self._prepare_field_lists()
+        self._generate_through_model_map()
 
         # Generate non-rename model operations
         self.generate_deleted_models()
@@ -207,7 +181,6 @@ class MigrationAutodetector(object):
         # isn't bad, but we need to pull a few things around so FKs work nicely
         # inside the same app
         for app_label, ops in sorted(self.generated_operations.items()):
-
             # construct a dependency graph for intra-app dependencies
             dependency_graph = {op: set() for op in ops}
             for op in ops:
@@ -220,15 +193,74 @@ class MigrationAutodetector(object):
             # we use a stable sort for deterministic tests & general behavior
             self.generated_operations[app_label] = stable_topological_sort(ops, dependency_graph)
 
-        # Now, we need to chop the lists of operations up into migrations with
-        # dependencies on each other.
-        # We do this by stepping up an app's list of operations until we
-        # find one that has an outgoing dependency that isn't in another app's
-        # migration yet (hasn't been chopped off its list). We then chop off the
-        # operations before it into a migration and move onto the next app.
-        # If we loop back around without doing anything, there's a circular
-        # dependency (which _should_ be impossible as the operations are all
-        # split at this point so they can't depend and be depended on)
+        self._build_migration_list(graph)
+
+        # OK, add in internal dependencies among the migrations
+        for app_label, migrations in self.migrations.items():
+            for m1, m2 in zip(migrations, migrations[1:]):
+                m2.dependencies.append((app_label, m1.name))
+
+        # De-dupe dependencies
+        for app_label, migrations in self.migrations.items():
+            for migration in migrations:
+                migration.dependencies = list(set(migration.dependencies))
+
+        # Optimize migrations
+        for app_label, migrations in self.migrations.items():
+            for migration in migrations:
+                migration.operations = MigrationOptimizer().optimize(migration.operations, app_label=app_label)
+
+        return self.migrations
+
+    def _prepare_field_lists(self):
+        """
+        Prepare field lists, and prepare a list of the fields that used
+        through models in the old state so we can make dependencies
+        from the through model deletion to the field that uses it.
+        """
+
+        self.kept_model_keys = set(self.old_model_keys).intersection(self.new_model_keys)
+        self.kept_proxy_keys = set(self.old_proxy_keys).intersection(self.new_proxy_keys)
+        self.kept_unmanaged_keys = set(self.old_unmanaged_keys).intersection(self.new_unmanaged_keys)
+        self.through_users = {}
+        self.old_field_keys = set()
+        self.new_field_keys = set()
+        for app_label, model_name in sorted(self.kept_model_keys):
+            old_model_name = self.renamed_models.get((app_label, model_name), model_name)
+            old_model_state = self.from_state.models[app_label, old_model_name]
+            new_model_state = self.to_state.models[app_label, model_name]
+            self.old_field_keys.update((app_label, model_name, x) for x, y in old_model_state.fields)
+            self.new_field_keys.update((app_label, model_name, x) for x, y in new_model_state.fields)
+
+    def _generate_through_model_map(self):
+        """
+        Through model map generation
+        """
+
+        for app_label, model_name in sorted(self.old_model_keys):
+            old_model_name = self.renamed_models.get((app_label, model_name), model_name)
+            old_model_state = self.from_state.models[app_label, old_model_name]
+            for field_name, field in old_model_state.fields:
+                old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name)
+                if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None)
+                        and not old_field.remote_field.through._meta.auto_created):
+                    through_key = (
+                        old_field.remote_field.through._meta.app_label,
+                        old_field.remote_field.through._meta.model_name,
+                    )
+                    self.through_users[through_key] = (app_label, old_model_name, field_name)
+
+    def _build_migration_list(self, graph=None):
+        """
+        We need to chop the lists of operations up into migrations with
+        dependencies on each other. We do this by stepping up an app's list of
+        operations until we find one that has an outgoing dependency that isn't
+        in another app's migration yet (hasn't been chopped off its list). We
+        then chop off the operations before it into a migration and move onto
+        the next app. If we loop back around without doing anything, there's a
+        circular dependency (which _should_ be impossible as the operations are
+        all split at this point so they can't depend and be depended on).
+        """
 
         self.migrations = {}
         num_ops = sum(len(x) for x in self.generated_operations.values())
@@ -309,23 +341,6 @@ class MigrationAutodetector(object):
                     raise ValueError("Cannot resolve operation dependencies: %r" % self.generated_operations)
             num_ops = new_num_ops
 
-        # OK, add in internal dependencies among the migrations
-        for app_label, migrations in self.migrations.items():
-            for m1, m2 in zip(migrations, migrations[1:]):
-                m2.dependencies.append((app_label, m1.name))
-
-        # De-dupe dependencies
-        for app_label, migrations in self.migrations.items():
-            for migration in migrations:
-                migration.dependencies = list(set(migration.dependencies))
-
-        # Optimize migrations
-        for app_label, migrations in self.migrations.items():
-            for migration in migrations:
-                migration.operations = MigrationOptimizer().optimize(migration.operations, app_label=app_label)
-
-        return self.migrations
-
     def check_dependency(self, operation, dependency):
         """
         Returns ``True`` if the given operation depends on the given dependency,
-- 
2.3.2 (Apple Git-55)

