Loading django/db/migrations/autodetector.py +70 −52 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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] Loading @@ -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 Loading Loading @@ -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)) Loading @@ -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, Loading Loading
django/db/migrations/autodetector.py +70 −52 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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] Loading @@ -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 Loading Loading @@ -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)) Loading @@ -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, Loading