mirror of
https://github.com/django/django.git
synced 2025-02-01 13:19:18 +00:00
Fixed #34980 -- Changed migration operation dependencies to namedtuples.
This commit is contained in:
parent
09b4a4e2c1
commit
7dd3e694db
@ -1,6 +1,7 @@
|
||||
import functools
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from collections import defaultdict, namedtuple
|
||||
from enum import Enum
|
||||
from graphlib import TopologicalSorter
|
||||
from itertools import chain
|
||||
|
||||
@ -16,6 +17,26 @@ from django.db.migrations.utils import (
|
||||
RegexObject,
|
||||
resolve_relation,
|
||||
)
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
class OperationDependency(
|
||||
namedtuple("OperationDependency", "app_label model_name field_name type")
|
||||
):
|
||||
class Type(Enum):
|
||||
CREATE = 0
|
||||
REMOVE = 1
|
||||
ALTER = 2
|
||||
REMOVE_ORDER_WRT = 3
|
||||
ALTER_FOO_TOGETHER = 4
|
||||
|
||||
@cached_property
|
||||
def model_name_lower(self):
|
||||
return self.model_name.lower()
|
||||
|
||||
@cached_property
|
||||
def field_name_lower(self):
|
||||
return self.field_name.lower()
|
||||
|
||||
|
||||
class MigrationAutodetector:
|
||||
@ -255,12 +276,20 @@ class MigrationAutodetector:
|
||||
Return the resolved dependency and a boolean denoting whether or not
|
||||
it was swappable.
|
||||
"""
|
||||
if dependency[0] != "__setting__":
|
||||
if dependency.app_label != "__setting__":
|
||||
return dependency, False
|
||||
resolved_app_label, resolved_object_name = getattr(
|
||||
settings, dependency[1]
|
||||
settings, dependency.model_name
|
||||
).split(".")
|
||||
return (resolved_app_label, resolved_object_name.lower()) + dependency[2:], True
|
||||
return (
|
||||
OperationDependency(
|
||||
resolved_app_label,
|
||||
resolved_object_name.lower(),
|
||||
dependency.field_name,
|
||||
dependency.type,
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
def _build_migration_list(self, graph=None):
|
||||
"""
|
||||
@ -296,11 +325,11 @@ class MigrationAutodetector:
|
||||
# swappable dependencies.
|
||||
original_dep = dep
|
||||
dep, is_swappable_dep = self._resolve_dependency(dep)
|
||||
if dep[0] != app_label:
|
||||
if dep.app_label != app_label:
|
||||
# External app dependency. See if it's not yet
|
||||
# satisfied.
|
||||
for other_operation in self.generated_operations.get(
|
||||
dep[0], []
|
||||
dep.app_label, []
|
||||
):
|
||||
if self.check_dependency(other_operation, dep):
|
||||
deps_satisfied = False
|
||||
@ -310,11 +339,17 @@ class MigrationAutodetector:
|
||||
else:
|
||||
if is_swappable_dep:
|
||||
operation_dependencies.add(
|
||||
(original_dep[0], original_dep[1])
|
||||
(
|
||||
original_dep.app_label,
|
||||
original_dep.model_name,
|
||||
)
|
||||
)
|
||||
elif dep[0] in self.migrations:
|
||||
elif dep.app_label in self.migrations:
|
||||
operation_dependencies.add(
|
||||
(dep[0], self.migrations[dep[0]][-1].name)
|
||||
(
|
||||
dep.app_label,
|
||||
self.migrations[dep.app_label][-1].name,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# If we can't find the other app, we add a
|
||||
@ -328,13 +363,13 @@ class MigrationAutodetector:
|
||||
# contains the target field. If it's
|
||||
# not yet migrated or has no
|
||||
# migrations, we use __first__.
|
||||
if graph and graph.leaf_nodes(dep[0]):
|
||||
if graph and graph.leaf_nodes(dep.app_label):
|
||||
operation_dependencies.add(
|
||||
graph.leaf_nodes(dep[0])[0]
|
||||
graph.leaf_nodes(dep.app_label)[0]
|
||||
)
|
||||
else:
|
||||
operation_dependencies.add(
|
||||
(dep[0], "__first__")
|
||||
(dep.app_label, "__first__")
|
||||
)
|
||||
else:
|
||||
deps_satisfied = False
|
||||
@ -389,7 +424,7 @@ class MigrationAutodetector:
|
||||
# Resolve intra-app dependencies to handle circular
|
||||
# references involving a swappable model.
|
||||
dep = self._resolve_dependency(dep)[0]
|
||||
if dep[0] != app_label:
|
||||
if dep.app_label != app_label:
|
||||
continue
|
||||
ts.add(op, *(x for x in ops if self.check_dependency(x, dep)))
|
||||
self.generated_operations[app_label] = list(ts.static_order())
|
||||
@ -418,58 +453,79 @@ class MigrationAutodetector:
|
||||
False otherwise.
|
||||
"""
|
||||
# Created model
|
||||
if dependency[2] is None and dependency[3] is True:
|
||||
if (
|
||||
dependency.field_name is None
|
||||
and dependency.type == OperationDependency.Type.CREATE
|
||||
):
|
||||
return (
|
||||
isinstance(operation, operations.CreateModel)
|
||||
and operation.name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency.model_name_lower
|
||||
)
|
||||
# Created field
|
||||
elif dependency[2] is not None and dependency[3] is True:
|
||||
elif (
|
||||
dependency.field_name is not None
|
||||
and dependency.type == OperationDependency.Type.CREATE
|
||||
):
|
||||
return (
|
||||
isinstance(operation, operations.CreateModel)
|
||||
and operation.name_lower == dependency[1].lower()
|
||||
and any(dependency[2] == x for x, y in operation.fields)
|
||||
and operation.name_lower == dependency.model_name_lower
|
||||
and any(dependency.field_name == x for x, y in operation.fields)
|
||||
) or (
|
||||
isinstance(operation, operations.AddField)
|
||||
and operation.model_name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency[2].lower()
|
||||
and operation.model_name_lower == dependency.model_name_lower
|
||||
and operation.name_lower == dependency.field_name_lower
|
||||
)
|
||||
# Removed field
|
||||
elif dependency[2] is not None and dependency[3] is False:
|
||||
elif (
|
||||
dependency.field_name is not None
|
||||
and dependency.type == OperationDependency.Type.REMOVE
|
||||
):
|
||||
return (
|
||||
isinstance(operation, operations.RemoveField)
|
||||
and operation.model_name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency[2].lower()
|
||||
and operation.model_name_lower == dependency.model_name_lower
|
||||
and operation.name_lower == dependency.field_name_lower
|
||||
)
|
||||
# Removed model
|
||||
elif dependency[2] is None and dependency[3] is False:
|
||||
elif (
|
||||
dependency.field_name is None
|
||||
and dependency.type == OperationDependency.Type.REMOVE
|
||||
):
|
||||
return (
|
||||
isinstance(operation, operations.DeleteModel)
|
||||
and operation.name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency.model_name_lower
|
||||
)
|
||||
# Field being altered
|
||||
elif dependency[2] is not None and dependency[3] == "alter":
|
||||
elif (
|
||||
dependency.field_name is not None
|
||||
and dependency.type == OperationDependency.Type.ALTER
|
||||
):
|
||||
return (
|
||||
isinstance(operation, operations.AlterField)
|
||||
and operation.model_name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency[2].lower()
|
||||
and operation.model_name_lower == dependency.model_name_lower
|
||||
and operation.name_lower == dependency.field_name_lower
|
||||
)
|
||||
# order_with_respect_to being unset for a field
|
||||
elif dependency[2] is not None and dependency[3] == "order_wrt_unset":
|
||||
elif (
|
||||
dependency.field_name is not None
|
||||
and dependency.type == OperationDependency.Type.REMOVE_ORDER_WRT
|
||||
):
|
||||
return (
|
||||
isinstance(operation, operations.AlterOrderWithRespectTo)
|
||||
and operation.name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency.model_name_lower
|
||||
and (operation.order_with_respect_to or "").lower()
|
||||
!= dependency[2].lower()
|
||||
!= dependency.field_name_lower
|
||||
)
|
||||
# Field is removed and part of an index/unique_together
|
||||
elif dependency[2] is not None and dependency[3] == "foo_together_change":
|
||||
elif (
|
||||
dependency.field_name is not None
|
||||
and dependency.type == OperationDependency.Type.ALTER_FOO_TOGETHER
|
||||
):
|
||||
return (
|
||||
isinstance(
|
||||
operation,
|
||||
(operations.AlterUniqueTogether, operations.AlterIndexTogether),
|
||||
)
|
||||
and operation.name_lower == dependency[1].lower()
|
||||
and operation.name_lower == dependency.model_name_lower
|
||||
)
|
||||
# Unknown dependency. Raise an error.
|
||||
else:
|
||||
@ -615,13 +671,22 @@ class MigrationAutodetector:
|
||||
)
|
||||
# Depend on the deletion of any possible proxy version of us
|
||||
dependencies = [
|
||||
(app_label, model_name, None, False),
|
||||
OperationDependency(
|
||||
app_label, model_name, None, OperationDependency.Type.REMOVE
|
||||
),
|
||||
]
|
||||
# Depend on all bases
|
||||
for base in model_state.bases:
|
||||
if isinstance(base, str) and "." in base:
|
||||
base_app_label, base_name = base.split(".", 1)
|
||||
dependencies.append((base_app_label, base_name, None, True))
|
||||
dependencies.append(
|
||||
OperationDependency(
|
||||
base_app_label,
|
||||
base_name,
|
||||
None,
|
||||
OperationDependency.Type.CREATE,
|
||||
)
|
||||
)
|
||||
# Depend on the removal of base fields if the new model has
|
||||
# a field with the same name.
|
||||
old_base_model_state = self.from_state.models.get(
|
||||
@ -640,17 +705,21 @@ class MigrationAutodetector:
|
||||
)
|
||||
for removed_base_field in removed_base_fields:
|
||||
dependencies.append(
|
||||
(base_app_label, base_name, removed_base_field, False)
|
||||
OperationDependency(
|
||||
base_app_label,
|
||||
base_name,
|
||||
removed_base_field,
|
||||
OperationDependency.Type.REMOVE,
|
||||
)
|
||||
)
|
||||
# Depend on the other end of the primary key if it's a relation
|
||||
if primary_key_rel:
|
||||
dependencies.append(
|
||||
resolve_relation(
|
||||
primary_key_rel,
|
||||
app_label,
|
||||
model_name,
|
||||
)
|
||||
+ (None, True)
|
||||
OperationDependency(
|
||||
*resolve_relation(primary_key_rel, app_label, model_name),
|
||||
None,
|
||||
OperationDependency.Type.CREATE,
|
||||
),
|
||||
)
|
||||
# Generate creation operation
|
||||
self.add_operation(
|
||||
@ -683,7 +752,11 @@ class MigrationAutodetector:
|
||||
self.to_state,
|
||||
)
|
||||
# Depend on our own model being created
|
||||
dependencies.append((app_label, model_name, None, True))
|
||||
dependencies.append(
|
||||
OperationDependency(
|
||||
app_label, model_name, None, OperationDependency.Type.CREATE
|
||||
)
|
||||
)
|
||||
# Make operation
|
||||
self.add_operation(
|
||||
app_label,
|
||||
@ -703,14 +776,28 @@ class MigrationAutodetector:
|
||||
order_with_respect_to=order_with_respect_to,
|
||||
),
|
||||
dependencies=[
|
||||
(app_label, model_name, order_with_respect_to, True),
|
||||
(app_label, model_name, None, True),
|
||||
OperationDependency(
|
||||
app_label,
|
||||
model_name,
|
||||
order_with_respect_to,
|
||||
OperationDependency.Type.CREATE,
|
||||
),
|
||||
OperationDependency(
|
||||
app_label, model_name, None, OperationDependency.Type.CREATE
|
||||
),
|
||||
],
|
||||
)
|
||||
related_dependencies = [
|
||||
(app_label, model_name, name, True) for name in sorted(related_fields)
|
||||
OperationDependency(
|
||||
app_label, model_name, name, OperationDependency.Type.CREATE
|
||||
)
|
||||
for name in sorted(related_fields)
|
||||
]
|
||||
related_dependencies.append((app_label, model_name, None, True))
|
||||
related_dependencies.append(
|
||||
OperationDependency(
|
||||
app_label, model_name, None, OperationDependency.Type.CREATE
|
||||
)
|
||||
)
|
||||
for index in indexes:
|
||||
self.add_operation(
|
||||
app_label,
|
||||
@ -754,7 +841,14 @@ class MigrationAutodetector:
|
||||
name=related_field_name,
|
||||
field=related_field,
|
||||
),
|
||||
dependencies=[(app_label, model_name, None, True)],
|
||||
dependencies=[
|
||||
OperationDependency(
|
||||
app_label,
|
||||
model_name,
|
||||
None,
|
||||
OperationDependency.Type.CREATE,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
def generate_created_proxies(self):
|
||||
@ -769,13 +863,22 @@ class MigrationAutodetector:
|
||||
assert model_state.options.get("proxy")
|
||||
# Depend on the deletion of any possible non-proxy version of us
|
||||
dependencies = [
|
||||
(app_label, model_name, None, False),
|
||||
OperationDependency(
|
||||
app_label, model_name, None, OperationDependency.Type.REMOVE
|
||||
),
|
||||
]
|
||||
# Depend on all bases
|
||||
for base in model_state.bases:
|
||||
if isinstance(base, str) and "." in base:
|
||||
base_app_label, base_name = base.split(".", 1)
|
||||
dependencies.append((base_app_label, base_name, None, True))
|
||||
dependencies.append(
|
||||
OperationDependency(
|
||||
base_app_label,
|
||||
base_name,
|
||||
None,
|
||||
OperationDependency.Type.CREATE,
|
||||
)
|
||||
)
|
||||
# Generate creation operation
|
||||
self.add_operation(
|
||||
app_label,
|
||||
@ -847,25 +950,34 @@ class MigrationAutodetector:
|
||||
), relation_related_fields in relations[app_label, model_name].items():
|
||||
for field_name, field in relation_related_fields.items():
|
||||
dependencies.append(
|
||||
(related_object_app_label, object_name, field_name, False),
|
||||
OperationDependency(
|
||||
related_object_app_label,
|
||||
object_name,
|
||||
field_name,
|
||||
OperationDependency.Type.REMOVE,
|
||||
),
|
||||
)
|
||||
if not field.many_to_many:
|
||||
dependencies.append(
|
||||
(
|
||||
OperationDependency(
|
||||
related_object_app_label,
|
||||
object_name,
|
||||
field_name,
|
||||
"alter",
|
||||
OperationDependency.Type.ALTER,
|
||||
),
|
||||
)
|
||||
|
||||
for name in sorted(related_fields):
|
||||
dependencies.append((app_label, model_name, name, False))
|
||||
dependencies.append(
|
||||
OperationDependency(
|
||||
app_label, model_name, name, OperationDependency.Type.REMOVE
|
||||
)
|
||||
)
|
||||
# We're referenced in another field's through=
|
||||
through_user = self.through_users.get((app_label, model_state.name_lower))
|
||||
if through_user:
|
||||
dependencies.append(
|
||||
(through_user[0], through_user[1], through_user[2], False)
|
||||
OperationDependency(*through_user, OperationDependency.Type.REMOVE),
|
||||
)
|
||||
# Finally, make the operation, deduping any dependencies
|
||||
self.add_operation(
|
||||
@ -998,7 +1110,11 @@ class MigrationAutodetector:
|
||||
def _generate_added_field(self, app_label, model_name, field_name):
|
||||
field = self.to_state.models[app_label, model_name].get_field(field_name)
|
||||
# Adding a field always depends at least on its removal.
|
||||
dependencies = [(app_label, model_name, field_name, False)]
|
||||
dependencies = [
|
||||
OperationDependency(
|
||||
app_label, model_name, field_name, OperationDependency.Type.REMOVE
|
||||
)
|
||||
]
|
||||
# Fields that are foreignkeys/m2ms depend on stuff.
|
||||
if field.remote_field and field.remote_field.model:
|
||||
dependencies.extend(
|
||||
@ -1065,8 +1181,18 @@ class MigrationAutodetector:
|
||||
# order_with_respect_to or index/unique_together operation;
|
||||
# this is safely ignored if there isn't one
|
||||
dependencies=[
|
||||
(app_label, model_name, field_name, "order_wrt_unset"),
|
||||
(app_label, model_name, field_name, "foo_together_change"),
|
||||
OperationDependency(
|
||||
app_label,
|
||||
model_name,
|
||||
field_name,
|
||||
OperationDependency.Type.REMOVE_ORDER_WRT,
|
||||
),
|
||||
OperationDependency(
|
||||
app_label,
|
||||
model_name,
|
||||
field_name,
|
||||
OperationDependency.Type.ALTER_FOO_TOGETHER,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
@ -1399,14 +1525,25 @@ class MigrationAutodetector:
|
||||
app_label,
|
||||
model_name,
|
||||
)
|
||||
dependencies = [(dep_app_label, dep_object_name, None, True)]
|
||||
dependencies = [
|
||||
OperationDependency(
|
||||
dep_app_label, dep_object_name, None, OperationDependency.Type.CREATE
|
||||
)
|
||||
]
|
||||
if getattr(field.remote_field, "through", None):
|
||||
through_app_label, through_object_name = resolve_relation(
|
||||
field.remote_field.through,
|
||||
app_label,
|
||||
model_name,
|
||||
)
|
||||
dependencies.append((through_app_label, through_object_name, None, True))
|
||||
dependencies.append(
|
||||
OperationDependency(
|
||||
through_app_label,
|
||||
through_object_name,
|
||||
None,
|
||||
OperationDependency.Type.CREATE,
|
||||
)
|
||||
)
|
||||
return dependencies
|
||||
|
||||
def _get_dependencies_for_model(self, app_label, model_name):
|
||||
@ -1617,11 +1754,11 @@ class MigrationAutodetector:
|
||||
dependencies = []
|
||||
if new_model_state.options.get("order_with_respect_to"):
|
||||
dependencies.append(
|
||||
(
|
||||
OperationDependency(
|
||||
app_label,
|
||||
model_name,
|
||||
new_model_state.options["order_with_respect_to"],
|
||||
True,
|
||||
OperationDependency.Type.CREATE,
|
||||
)
|
||||
)
|
||||
# Actually generate the operation
|
||||
|
Loading…
x
Reference in New Issue
Block a user