mirror of
https://github.com/django/django.git
synced 2024-12-01 15:42:04 +01:00
Fixed #24899 -- Split migrations autodetector._detect_changes() method
This commit is contained in:
parent
2dc93bb10a
commit
ed63df3fc2
@ -155,35 +155,9 @@ class MigrationAutodetector(object):
|
|||||||
# Renames have to come first
|
# Renames have to come first
|
||||||
self.generate_renamed_models()
|
self.generate_renamed_models()
|
||||||
|
|
||||||
# Prepare field lists, and prepare a list of the fields that used
|
# Prepare lists of fields and generate through model map
|
||||||
# through models in the old state so we can make dependencies
|
self._prepare_field_lists()
|
||||||
# from the through model deletion to the field that uses it.
|
self._generate_through_model_map()
|
||||||
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)
|
|
||||||
|
|
||||||
# Generate non-rename model operations
|
# Generate non-rename model operations
|
||||||
self.generate_deleted_models()
|
self.generate_deleted_models()
|
||||||
@ -203,33 +177,59 @@ class MigrationAutodetector(object):
|
|||||||
self.generate_altered_db_table()
|
self.generate_altered_db_table()
|
||||||
self.generate_altered_order_with_respect_to()
|
self.generate_altered_order_with_respect_to()
|
||||||
|
|
||||||
# Now, reordering to make things possible. The order we have already
|
self._sort_migrations()
|
||||||
# isn't bad, but we need to pull a few things around so FKs work nicely
|
self._build_migration_list(graph)
|
||||||
# inside the same app
|
self._optimize_migrations()
|
||||||
for app_label, ops in sorted(self.generated_operations.items()):
|
|
||||||
|
|
||||||
# construct a dependency graph for intra-app dependencies
|
return self.migrations
|
||||||
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
|
def _prepare_field_lists(self):
|
||||||
self.generated_operations[app_label] = stable_topological_sort(ops, dependency_graph)
|
"""
|
||||||
|
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)
|
||||||
|
|
||||||
# Now, we need to chop the lists of operations up into migrations with
|
def _generate_through_model_map(self):
|
||||||
# dependencies on each other.
|
"""
|
||||||
# We do this by stepping up an app's list of operations until we
|
Through model map generation
|
||||||
# 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
|
for app_label, model_name in sorted(self.old_model_keys):
|
||||||
# operations before it into a migration and move onto the next app.
|
old_model_name = self.renamed_models.get((app_label, model_name), model_name)
|
||||||
# If we loop back around without doing anything, there's a circular
|
old_model_state = self.from_state.models[app_label, old_model_name]
|
||||||
# dependency (which _should_ be impossible as the operations are all
|
for field_name, field in old_model_state.fields:
|
||||||
# split at this point so they can't depend and be depended on)
|
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 = {}
|
self.migrations = {}
|
||||||
num_ops = sum(len(x) for x in self.generated_operations.values())
|
num_ops = sum(len(x) for x in self.generated_operations.values())
|
||||||
chop_mode = False
|
chop_mode = False
|
||||||
@ -309,7 +309,27 @@ class MigrationAutodetector(object):
|
|||||||
raise ValueError("Cannot resolve operation dependencies: %r" % self.generated_operations)
|
raise ValueError("Cannot resolve operation dependencies: %r" % self.generated_operations)
|
||||||
num_ops = new_num_ops
|
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 app_label, migrations in self.migrations.items():
|
||||||
for m1, m2 in zip(migrations, migrations[1:]):
|
for m1, m2 in zip(migrations, migrations[1:]):
|
||||||
m2.dependencies.append((app_label, m1.name))
|
m2.dependencies.append((app_label, m1.name))
|
||||||
@ -324,8 +344,6 @@ class MigrationAutodetector(object):
|
|||||||
for migration in migrations:
|
for migration in migrations:
|
||||||
migration.operations = MigrationOptimizer().optimize(migration.operations, app_label=app_label)
|
migration.operations = MigrationOptimizer().optimize(migration.operations, app_label=app_label)
|
||||||
|
|
||||||
return self.migrations
|
|
||||||
|
|
||||||
def check_dependency(self, operation, dependency):
|
def check_dependency(self, operation, dependency):
|
||||||
"""
|
"""
|
||||||
Returns ``True`` if the given operation depends on the given dependency,
|
Returns ``True`` if the given operation depends on the given dependency,
|
||||||
|
Loading…
Reference in New Issue
Block a user