diff options
| author | Steadman <mark@steadman.io> | 2015-06-04 14:47:44 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2015-06-04 22:35:33 -0400 |
| commit | ed63df3fc2f0c228354ca3435e9bed45be9d0909 (patch) | |
| tree | 76efeb22f7c4a577f76160d99857ffea415ea20e | |
| parent | 2dc93bb10ab1c2af1df9977f1b51d47cadab7b21 (diff) | |
Fixed #24899 -- Split migrations autodetector._detect_changes() method
| -rw-r--r-- | django/db/migrations/autodetector.py | 122 |
1 files changed, 70 insertions, 52 deletions
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 9a4ba42722..ddf8e36b83 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -155,9 +155,40 @@ 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. + # 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() + self.generate_created_models() + self.generate_deleted_proxies() + self.generate_created_proxies() + self.generate_altered_options() + self.generate_altered_managers() + + # Generate field operations + self.generate_renamed_fields() + self.generate_removed_fields() + self.generate_added_fields() + self.generate_altered_fields() + self.generate_altered_unique_together() + self.generate_altered_index_together() + self.generate_altered_db_table() + self.generate_altered_order_with_respect_to() + + self._sort_migrations() + self._build_migration_list(graph) + self._optimize_migrations() + + 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) @@ -171,7 +202,10 @@ class MigrationAutodetector(object): 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 + 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] @@ -185,51 +219,17 @@ class MigrationAutodetector(object): ) self.through_users[through_key] = (app_label, old_model_name, field_name) - # Generate non-rename model operations - self.generate_deleted_models() - self.generate_created_models() - self.generate_deleted_proxies() - self.generate_created_proxies() - self.generate_altered_options() - self.generate_altered_managers() - - # Generate field operations - self.generate_renamed_fields() - self.generate_removed_fields() - self.generate_added_fields() - self.generate_altered_fields() - self.generate_altered_unique_together() - self.generate_altered_index_together() - self.generate_altered_db_table() - self.generate_altered_order_with_respect_to() - - # Now, reordering to make things possible. The order we have already - # 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: - for dep in op._auto_deps: - if dep[0] == app_label: - for op2 in ops: - if self.check_dependency(op2, dep): - dependency_graph[op].add(op2) - - # 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) - + 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()) chop_mode = False @@ -309,7 +309,27 @@ 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 + def _sort_migrations(self): + """ + Reorder to make things possible. The order we have already 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: + for dep in op._auto_deps: + if dep[0] == app_label: + for op2 in ops: + if self.check_dependency(op2, dep): + dependency_graph[op].add(op2) + + # we use a stable sort for deterministic tests & general behavior + self.generated_operations[app_label] = stable_topological_sort(ops, dependency_graph) + + def _optimize_migrations(self): + # 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)) @@ -324,8 +344,6 @@ class MigrationAutodetector(object): 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, |
