Fixed #31499 -- Stored ModelState.fields into a dict.
This allows the removal of its O(n) .get_field_by_name method and many other awkward access patterns. While fields were initially stored in a list to preserve the initial model definiton field ordering the auto-detector doesn't take field ordering into account and no operations exists to reorder fields of a model. This makes the preservation of the field ordering completely superflous because field reorganization after the creation of the model state wouldn't be taken into account.
This commit is contained in:
parent
696024fb73
commit
06889d6206
|
@ -93,7 +93,7 @@ class MigrationAutodetector:
|
||||||
of course, the related fields change during renames).
|
of course, the related fields change during renames).
|
||||||
"""
|
"""
|
||||||
fields_def = []
|
fields_def = []
|
||||||
for name, field in sorted(fields):
|
for name, field in sorted(fields.items()):
|
||||||
deconstruction = self.deep_deconstruct(field)
|
deconstruction = self.deep_deconstruct(field)
|
||||||
if field.remote_field and field.remote_field.model:
|
if field.remote_field and field.remote_field.model:
|
||||||
del deconstruction[2]['to']
|
del deconstruction[2]['to']
|
||||||
|
@ -208,17 +208,17 @@ class MigrationAutodetector:
|
||||||
self.kept_unmanaged_keys = self.old_unmanaged_keys & self.new_unmanaged_keys
|
self.kept_unmanaged_keys = self.old_unmanaged_keys & self.new_unmanaged_keys
|
||||||
self.through_users = {}
|
self.through_users = {}
|
||||||
self.old_field_keys = {
|
self.old_field_keys = {
|
||||||
(app_label, model_name, x)
|
(app_label, model_name, field_name)
|
||||||
for app_label, model_name in self.kept_model_keys
|
for app_label, model_name in self.kept_model_keys
|
||||||
for x, y in self.from_state.models[
|
for field_name in self.from_state.models[
|
||||||
app_label,
|
app_label,
|
||||||
self.renamed_models.get((app_label, model_name), model_name)
|
self.renamed_models.get((app_label, model_name), model_name)
|
||||||
].fields
|
].fields
|
||||||
}
|
}
|
||||||
self.new_field_keys = {
|
self.new_field_keys = {
|
||||||
(app_label, model_name, x)
|
(app_label, model_name, field_name)
|
||||||
for app_label, model_name in self.kept_model_keys
|
for app_label, model_name in self.kept_model_keys
|
||||||
for x, y in self.to_state.models[app_label, model_name].fields
|
for field_name in self.to_state.models[app_label, model_name].fields
|
||||||
}
|
}
|
||||||
|
|
||||||
def _generate_through_model_map(self):
|
def _generate_through_model_map(self):
|
||||||
|
@ -226,7 +226,7 @@ class MigrationAutodetector:
|
||||||
for app_label, model_name in sorted(self.old_model_keys):
|
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_name = self.renamed_models.get((app_label, model_name), model_name)
|
||||||
old_model_state = self.from_state.models[app_label, old_model_name]
|
old_model_state = self.from_state.models[app_label, old_model_name]
|
||||||
for field_name, field in old_model_state.fields:
|
for field_name in old_model_state.fields:
|
||||||
old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field(field_name)
|
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
|
if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None) and
|
||||||
not old_field.remote_field.through._meta.auto_created):
|
not old_field.remote_field.through._meta.auto_created):
|
||||||
|
@ -576,7 +576,7 @@ class MigrationAutodetector:
|
||||||
app_label,
|
app_label,
|
||||||
operations.CreateModel(
|
operations.CreateModel(
|
||||||
name=model_state.name,
|
name=model_state.name,
|
||||||
fields=[d for d in model_state.fields if d[0] not in related_fields],
|
fields=[d for d in model_state.fields.items() if d[0] not in related_fields],
|
||||||
options=model_state.options,
|
options=model_state.options,
|
||||||
bases=model_state.bases,
|
bases=model_state.bases,
|
||||||
managers=model_state.managers,
|
managers=model_state.managers,
|
||||||
|
@ -820,7 +820,7 @@ class MigrationAutodetector:
|
||||||
field_dec = self.deep_deconstruct(field)
|
field_dec = self.deep_deconstruct(field)
|
||||||
for rem_app_label, rem_model_name, rem_field_name in sorted(self.old_field_keys - self.new_field_keys):
|
for rem_app_label, rem_model_name, rem_field_name in sorted(self.old_field_keys - self.new_field_keys):
|
||||||
if rem_app_label == app_label and rem_model_name == model_name:
|
if rem_app_label == app_label and rem_model_name == model_name:
|
||||||
old_field = old_model_state.get_field_by_name(rem_field_name)
|
old_field = old_model_state.fields[rem_field_name]
|
||||||
old_field_dec = self.deep_deconstruct(old_field)
|
old_field_dec = self.deep_deconstruct(old_field)
|
||||||
if field.remote_field and field.remote_field.model and 'to' in old_field_dec[2]:
|
if field.remote_field and field.remote_field.model and 'to' in old_field_dec[2]:
|
||||||
old_rel_to = old_field_dec[2]['to']
|
old_rel_to = old_field_dec[2]['to']
|
||||||
|
|
|
@ -89,7 +89,7 @@ class AddField(FieldOperation):
|
||||||
field.default = NOT_PROVIDED
|
field.default = NOT_PROVIDED
|
||||||
else:
|
else:
|
||||||
field = self.field
|
field = self.field
|
||||||
state.models[app_label, self.model_name_lower].fields.append((self.name, field))
|
state.models[app_label, self.model_name_lower].fields[self.name] = field
|
||||||
# Delay rendering of relationships if it's not a relational field
|
# Delay rendering of relationships if it's not a relational field
|
||||||
delay = not field.is_relation
|
delay = not field.is_relation
|
||||||
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
||||||
|
@ -154,14 +154,8 @@ class RemoveField(FieldOperation):
|
||||||
)
|
)
|
||||||
|
|
||||||
def state_forwards(self, app_label, state):
|
def state_forwards(self, app_label, state):
|
||||||
new_fields = []
|
model_state = state.models[app_label, self.model_name_lower]
|
||||||
old_field = None
|
old_field = model_state.fields.pop(self.name)
|
||||||
for name, instance in state.models[app_label, self.model_name_lower].fields:
|
|
||||||
if name != self.name:
|
|
||||||
new_fields.append((name, instance))
|
|
||||||
else:
|
|
||||||
old_field = instance
|
|
||||||
state.models[app_label, self.model_name_lower].fields = new_fields
|
|
||||||
# Delay rendering of relationships if it's not a relational field
|
# Delay rendering of relationships if it's not a relational field
|
||||||
delay = not old_field.is_relation
|
delay = not old_field.is_relation
|
||||||
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
state.reload_model(app_label, self.model_name_lower, delay=delay)
|
||||||
|
@ -217,11 +211,8 @@ class AlterField(FieldOperation):
|
||||||
field.default = NOT_PROVIDED
|
field.default = NOT_PROVIDED
|
||||||
else:
|
else:
|
||||||
field = self.field
|
field = self.field
|
||||||
state.models[app_label, self.model_name_lower].fields = [
|
model_state = state.models[app_label, self.model_name_lower]
|
||||||
(n, field if n == self.name else f)
|
model_state.fields[self.name] = field
|
||||||
for n, f in
|
|
||||||
state.models[app_label, self.model_name_lower].fields
|
|
||||||
]
|
|
||||||
# TODO: investigate if old relational fields must be reloaded or if it's
|
# TODO: investigate if old relational fields must be reloaded or if it's
|
||||||
# sufficient if the new field is (#27737).
|
# sufficient if the new field is (#27737).
|
||||||
# Delay rendering of relationships if it's not a relational field and
|
# Delay rendering of relationships if it's not a relational field and
|
||||||
|
@ -299,11 +290,14 @@ class RenameField(FieldOperation):
|
||||||
model_state = state.models[app_label, self.model_name_lower]
|
model_state = state.models[app_label, self.model_name_lower]
|
||||||
# Rename the field
|
# Rename the field
|
||||||
fields = model_state.fields
|
fields = model_state.fields
|
||||||
found = None
|
try:
|
||||||
for index, (name, field) in enumerate(fields):
|
found = fields.pop(self.old_name)
|
||||||
if not found and name == self.old_name:
|
except KeyError:
|
||||||
fields[index] = (self.new_name, field)
|
raise FieldDoesNotExist(
|
||||||
found = field
|
"%s.%s has no field named '%s'" % (app_label, self.model_name, self.old_name)
|
||||||
|
)
|
||||||
|
fields[self.new_name] = found
|
||||||
|
for field in fields.values():
|
||||||
# Fix from_fields to refer to the new field.
|
# Fix from_fields to refer to the new field.
|
||||||
from_fields = getattr(field, 'from_fields', None)
|
from_fields = getattr(field, 'from_fields', None)
|
||||||
if from_fields:
|
if from_fields:
|
||||||
|
@ -311,10 +305,6 @@ class RenameField(FieldOperation):
|
||||||
self.new_name if from_field_name == self.old_name else from_field_name
|
self.new_name if from_field_name == self.old_name else from_field_name
|
||||||
for from_field_name in from_fields
|
for from_field_name in from_fields
|
||||||
])
|
])
|
||||||
if found is None:
|
|
||||||
raise FieldDoesNotExist(
|
|
||||||
"%s.%s has no field named '%s'" % (app_label, self.model_name, self.old_name)
|
|
||||||
)
|
|
||||||
# Fix index/unique_together to refer to the new field
|
# Fix index/unique_together to refer to the new field
|
||||||
options = model_state.options
|
options = model_state.options
|
||||||
for option in ('index_together', 'unique_together'):
|
for option in ('index_together', 'unique_together'):
|
||||||
|
|
|
@ -310,7 +310,7 @@ class RenameModel(ModelOperation):
|
||||||
old_model_tuple = (app_label, self.old_name_lower)
|
old_model_tuple = (app_label, self.old_name_lower)
|
||||||
new_remote_model = '%s.%s' % (app_label, self.new_name)
|
new_remote_model = '%s.%s' % (app_label, self.new_name)
|
||||||
to_reload = set()
|
to_reload = set()
|
||||||
for model_state, index, name, field, reference in get_references(state, old_model_tuple):
|
for model_state, name, field, reference in get_references(state, old_model_tuple):
|
||||||
changed_field = None
|
changed_field = None
|
||||||
if reference.to:
|
if reference.to:
|
||||||
changed_field = field.clone()
|
changed_field = field.clone()
|
||||||
|
@ -320,7 +320,7 @@ class RenameModel(ModelOperation):
|
||||||
changed_field = field.clone()
|
changed_field = field.clone()
|
||||||
changed_field.remote_field.through = new_remote_model
|
changed_field.remote_field.through = new_remote_model
|
||||||
if changed_field:
|
if changed_field:
|
||||||
model_state.fields[index] = name, changed_field
|
model_state.fields[name] = changed_field
|
||||||
to_reload.add((model_state.app_label, model_state.name_lower))
|
to_reload.add((model_state.app_label, model_state.name_lower))
|
||||||
# Reload models related to old model before removing the old model.
|
# Reload models related to old model before removing the old model.
|
||||||
state.reload_models(to_reload, delay=True)
|
state.reload_models(to_reload, delay=True)
|
||||||
|
|
|
@ -83,17 +83,17 @@ def field_references(
|
||||||
|
|
||||||
def get_references(state, model_tuple, field_tuple=()):
|
def get_references(state, model_tuple, field_tuple=()):
|
||||||
"""
|
"""
|
||||||
Generator of (model_state, index, name, field, reference) referencing
|
Generator of (model_state, name, field, reference) referencing
|
||||||
provided context.
|
provided context.
|
||||||
|
|
||||||
If field_tuple is provided only references to this particular field of
|
If field_tuple is provided only references to this particular field of
|
||||||
model_tuple will be generated.
|
model_tuple will be generated.
|
||||||
"""
|
"""
|
||||||
for state_model_tuple, model_state in state.models.items():
|
for state_model_tuple, model_state in state.models.items():
|
||||||
for index, (name, field) in enumerate(model_state.fields):
|
for name, field in model_state.fields.items():
|
||||||
reference = field_references(state_model_tuple, field, model_tuple, *field_tuple)
|
reference = field_references(state_model_tuple, field, model_tuple, *field_tuple)
|
||||||
if reference:
|
if reference:
|
||||||
yield model_state, index, name, field, reference
|
yield model_state, name, field, reference
|
||||||
|
|
||||||
|
|
||||||
def field_is_referenced(state, model_tuple, field_tuple):
|
def field_is_referenced(state, model_tuple, field_tuple):
|
||||||
|
|
|
@ -125,7 +125,7 @@ class ProjectState:
|
||||||
# Directly related models are the models pointed to by ForeignKeys,
|
# Directly related models are the models pointed to by ForeignKeys,
|
||||||
# OneToOneFields, and ManyToManyFields.
|
# OneToOneFields, and ManyToManyFields.
|
||||||
direct_related_models = set()
|
direct_related_models = set()
|
||||||
for name, field in model_state.fields:
|
for field in model_state.fields.values():
|
||||||
if field.is_relation:
|
if field.is_relation:
|
||||||
if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT:
|
if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT:
|
||||||
continue
|
continue
|
||||||
|
@ -359,16 +359,13 @@ class ModelState:
|
||||||
def __init__(self, app_label, name, fields, options=None, bases=None, managers=None):
|
def __init__(self, app_label, name, fields, options=None, bases=None, managers=None):
|
||||||
self.app_label = app_label
|
self.app_label = app_label
|
||||||
self.name = name
|
self.name = name
|
||||||
self.fields = fields
|
self.fields = dict(fields)
|
||||||
self.options = options or {}
|
self.options = options or {}
|
||||||
self.options.setdefault('indexes', [])
|
self.options.setdefault('indexes', [])
|
||||||
self.options.setdefault('constraints', [])
|
self.options.setdefault('constraints', [])
|
||||||
self.bases = bases or (models.Model,)
|
self.bases = bases or (models.Model,)
|
||||||
self.managers = managers or []
|
self.managers = managers or []
|
||||||
# Sanity-check that fields is NOT a dict. It must be ordered.
|
for name, field in self.fields.items():
|
||||||
if isinstance(self.fields, dict):
|
|
||||||
raise ValueError("ModelState.fields cannot be a dict - it must be a list of 2-tuples.")
|
|
||||||
for name, field in fields:
|
|
||||||
# Sanity-check that fields are NOT already bound to a model.
|
# Sanity-check that fields are NOT already bound to a model.
|
||||||
if hasattr(field, 'model'):
|
if hasattr(field, 'model'):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
@ -544,7 +541,7 @@ class ModelState:
|
||||||
return self.__class__(
|
return self.__class__(
|
||||||
app_label=self.app_label,
|
app_label=self.app_label,
|
||||||
name=self.name,
|
name=self.name,
|
||||||
fields=list(self.fields),
|
fields=dict(self.fields),
|
||||||
# Since options are shallow-copied here, operations such as
|
# Since options are shallow-copied here, operations such as
|
||||||
# AddIndex must replace their option (e.g 'indexes') rather
|
# AddIndex must replace their option (e.g 'indexes') rather
|
||||||
# than mutating it.
|
# than mutating it.
|
||||||
|
@ -566,8 +563,8 @@ class ModelState:
|
||||||
)
|
)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
raise InvalidBasesError("Cannot resolve one or more bases from %r" % (self.bases,))
|
raise InvalidBasesError("Cannot resolve one or more bases from %r" % (self.bases,))
|
||||||
# Turn fields into a dict for the body, add other bits
|
# Clone fields for the body, add other bits.
|
||||||
body = {name: field.clone() for name, field in self.fields}
|
body = {name: field.clone() for name, field in self.fields.items()}
|
||||||
body['Meta'] = meta
|
body['Meta'] = meta
|
||||||
body['__module__'] = "__fake__"
|
body['__module__'] = "__fake__"
|
||||||
|
|
||||||
|
@ -576,12 +573,6 @@ class ModelState:
|
||||||
# Then, make a Model object (apps.register_model is called in __new__)
|
# Then, make a Model object (apps.register_model is called in __new__)
|
||||||
return type(self.name, bases, body)
|
return type(self.name, bases, body)
|
||||||
|
|
||||||
def get_field_by_name(self, name):
|
|
||||||
for fname, field in self.fields:
|
|
||||||
if fname == name:
|
|
||||||
return field
|
|
||||||
raise ValueError("No field called %s on model %s" % (name, self.name))
|
|
||||||
|
|
||||||
def get_index_by_name(self, name):
|
def get_index_by_name(self, name):
|
||||||
for index in self.options['indexes']:
|
for index in self.options['indexes']:
|
||||||
if index.name == name:
|
if index.name == name:
|
||||||
|
@ -602,8 +593,13 @@ class ModelState:
|
||||||
(self.app_label == other.app_label) and
|
(self.app_label == other.app_label) and
|
||||||
(self.name == other.name) and
|
(self.name == other.name) and
|
||||||
(len(self.fields) == len(other.fields)) and
|
(len(self.fields) == len(other.fields)) and
|
||||||
all((k1 == k2 and (f1.deconstruct()[1:] == f2.deconstruct()[1:]))
|
all(
|
||||||
for (k1, f1), (k2, f2) in zip(sorted(self.fields), sorted(other.fields))) and
|
k1 == k2 and f1.deconstruct()[1:] == f2.deconstruct()[1:]
|
||||||
|
for (k1, f1), (k2, f2) in zip(
|
||||||
|
sorted(self.fields.items()),
|
||||||
|
sorted(other.fields.items()),
|
||||||
|
)
|
||||||
|
) and
|
||||||
(self.options == other.options) and
|
(self.options == other.options) and
|
||||||
(self.bases == other.bases) and
|
(self.bases == other.bases) and
|
||||||
(self.managers == other.managers)
|
(self.managers == other.managers)
|
||||||
|
|
|
@ -516,13 +516,13 @@ class ExecutorTests(MigrationTestBase):
|
||||||
state = executor.migrate([
|
state = executor.migrate([
|
||||||
('mutate_state_a', '0001_initial'),
|
('mutate_state_a', '0001_initial'),
|
||||||
])
|
])
|
||||||
self.assertIn('added', dict(state.models['mutate_state_b', 'b'].fields))
|
self.assertIn('added', state.models['mutate_state_b', 'b'].fields)
|
||||||
executor.loader.build_graph()
|
executor.loader.build_graph()
|
||||||
# Migrate backward.
|
# Migrate backward.
|
||||||
state = executor.migrate([
|
state = executor.migrate([
|
||||||
('mutate_state_a', None),
|
('mutate_state_a', None),
|
||||||
])
|
])
|
||||||
self.assertIn('added', dict(state.models['mutate_state_b', 'b'].fields))
|
self.assertIn('added', state.models['mutate_state_b', 'b'].fields)
|
||||||
executor.migrate([
|
executor.migrate([
|
||||||
('mutate_state_b', None),
|
('mutate_state_b', None),
|
||||||
])
|
])
|
||||||
|
|
|
@ -73,15 +73,12 @@ class LoaderTests(TestCase):
|
||||||
|
|
||||||
author_state = project_state.models["migrations", "author"]
|
author_state = project_state.models["migrations", "author"]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[x for x, y in author_state.fields],
|
list(author_state.fields),
|
||||||
["id", "name", "slug", "age", "rating"]
|
["id", "name", "slug", "age", "rating"]
|
||||||
)
|
)
|
||||||
|
|
||||||
book_state = project_state.models["migrations", "book"]
|
book_state = project_state.models["migrations", "book"]
|
||||||
self.assertEqual(
|
self.assertEqual(list(book_state.fields), ['id', 'author'])
|
||||||
[x for x, y in book_state.fields],
|
|
||||||
["id", "author"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Ensure we've included unmigrated apps in there too
|
# Ensure we've included unmigrated apps in there too
|
||||||
self.assertIn("basic", project_state.real_apps)
|
self.assertIn("basic", project_state.real_apps)
|
||||||
|
@ -122,10 +119,7 @@ class LoaderTests(TestCase):
|
||||||
self.assertEqual(len([m for a, m in project_state.models if a == "migrations"]), 1)
|
self.assertEqual(len([m for a, m in project_state.models if a == "migrations"]), 1)
|
||||||
|
|
||||||
book_state = project_state.models["migrations", "book"]
|
book_state = project_state.models["migrations", "book"]
|
||||||
self.assertEqual(
|
self.assertEqual(list(book_state.fields), ['id', 'user'])
|
||||||
[x for x, y in book_state.fields],
|
|
||||||
["id", "user"]
|
|
||||||
)
|
|
||||||
|
|
||||||
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_run_before"})
|
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_run_before"})
|
||||||
def test_run_before(self):
|
def test_run_before(self):
|
||||||
|
|
|
@ -521,7 +521,10 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertNotIn(("test_rnmo", "pony"), new_state.models)
|
self.assertNotIn(("test_rnmo", "pony"), new_state.models)
|
||||||
self.assertIn(("test_rnmo", "horse"), new_state.models)
|
self.assertIn(("test_rnmo", "horse"), new_state.models)
|
||||||
# RenameModel also repoints all incoming FKs and M2Ms
|
# RenameModel also repoints all incoming FKs and M2Ms
|
||||||
self.assertEqual("test_rnmo.Horse", new_state.models["test_rnmo", "rider"].fields[1][1].remote_field.model)
|
self.assertEqual(
|
||||||
|
new_state.models['test_rnmo', 'rider'].fields['pony'].remote_field.model,
|
||||||
|
'test_rnmo.Horse',
|
||||||
|
)
|
||||||
self.assertTableNotExists("test_rnmo_pony")
|
self.assertTableNotExists("test_rnmo_pony")
|
||||||
self.assertTableExists("test_rnmo_horse")
|
self.assertTableExists("test_rnmo_horse")
|
||||||
if connection.features.supports_foreign_keys:
|
if connection.features.supports_foreign_keys:
|
||||||
|
@ -532,7 +535,10 @@ class OperationTests(OperationTestBase):
|
||||||
# Test original state and database
|
# Test original state and database
|
||||||
self.assertIn(("test_rnmo", "pony"), original_state.models)
|
self.assertIn(("test_rnmo", "pony"), original_state.models)
|
||||||
self.assertNotIn(("test_rnmo", "horse"), original_state.models)
|
self.assertNotIn(("test_rnmo", "horse"), original_state.models)
|
||||||
self.assertEqual("Pony", original_state.models["test_rnmo", "rider"].fields[1][1].remote_field.model)
|
self.assertEqual(
|
||||||
|
original_state.models['test_rnmo', 'rider'].fields['pony'].remote_field.model,
|
||||||
|
'Pony',
|
||||||
|
)
|
||||||
self.assertTableExists("test_rnmo_pony")
|
self.assertTableExists("test_rnmo_pony")
|
||||||
self.assertTableNotExists("test_rnmo_horse")
|
self.assertTableNotExists("test_rnmo_horse")
|
||||||
if connection.features.supports_foreign_keys:
|
if connection.features.supports_foreign_keys:
|
||||||
|
@ -579,7 +585,7 @@ class OperationTests(OperationTestBase):
|
||||||
# Remember, RenameModel also repoints all incoming FKs and M2Ms
|
# Remember, RenameModel also repoints all incoming FKs and M2Ms
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'self',
|
'self',
|
||||||
new_state.models["test_rmwsrf", "horserider"].fields[2][1].remote_field.model
|
new_state.models["test_rmwsrf", "horserider"].fields['friend'].remote_field.model
|
||||||
)
|
)
|
||||||
HorseRider = new_state.apps.get_model('test_rmwsrf', 'horserider')
|
HorseRider = new_state.apps.get_model('test_rmwsrf', 'horserider')
|
||||||
self.assertIs(HorseRider._meta.get_field('horserider').remote_field.model, HorseRider)
|
self.assertIs(HorseRider._meta.get_field('horserider').remote_field.model, HorseRider)
|
||||||
|
@ -621,8 +627,8 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertIn(("test_rmwsc", "littlehorse"), new_state.models)
|
self.assertIn(("test_rmwsc", "littlehorse"), new_state.models)
|
||||||
# RenameModel shouldn't repoint the superclass's relations, only local ones
|
# RenameModel shouldn't repoint the superclass's relations, only local ones
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
project_state.models["test_rmwsc", "rider"].fields[1][1].remote_field.model,
|
project_state.models['test_rmwsc', 'rider'].fields['pony'].remote_field.model,
|
||||||
new_state.models["test_rmwsc", "rider"].fields[1][1].remote_field.model
|
new_state.models['test_rmwsc', 'rider'].fields['pony'].remote_field.model,
|
||||||
)
|
)
|
||||||
# Before running the migration we have a table for Shetland Pony, not Little Horse
|
# Before running the migration we have a table for Shetland Pony, not Little Horse
|
||||||
self.assertTableExists("test_rmwsc_shetlandpony")
|
self.assertTableExists("test_rmwsc_shetlandpony")
|
||||||
|
@ -797,10 +803,7 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertEqual(operation.describe(), "Add field height to Pony")
|
self.assertEqual(operation.describe(), "Add field height to Pony")
|
||||||
project_state, new_state = self.make_test_state("test_adfl", operation)
|
project_state, new_state = self.make_test_state("test_adfl", operation)
|
||||||
self.assertEqual(len(new_state.models["test_adfl", "pony"].fields), 4)
|
self.assertEqual(len(new_state.models["test_adfl", "pony"].fields), 4)
|
||||||
field = [
|
field = new_state.models['test_adfl', 'pony'].fields['height']
|
||||||
f for n, f in new_state.models["test_adfl", "pony"].fields
|
|
||||||
if n == "height"
|
|
||||||
][0]
|
|
||||||
self.assertEqual(field.default, 5)
|
self.assertEqual(field.default, 5)
|
||||||
# Test the database alteration
|
# Test the database alteration
|
||||||
self.assertColumnNotExists("test_adfl_pony", "height")
|
self.assertColumnNotExists("test_adfl_pony", "height")
|
||||||
|
@ -974,10 +977,7 @@ class OperationTests(OperationTestBase):
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
operation.state_forwards("test_adflpd", new_state)
|
operation.state_forwards("test_adflpd", new_state)
|
||||||
self.assertEqual(len(new_state.models["test_adflpd", "pony"].fields), 4)
|
self.assertEqual(len(new_state.models["test_adflpd", "pony"].fields), 4)
|
||||||
field = [
|
field = new_state.models['test_adflpd', 'pony'].fields['height']
|
||||||
f for n, f in new_state.models["test_adflpd", "pony"].fields
|
|
||||||
if n == "height"
|
|
||||||
][0]
|
|
||||||
self.assertEqual(field.default, models.NOT_PROVIDED)
|
self.assertEqual(field.default, models.NOT_PROVIDED)
|
||||||
# Test the database alteration
|
# Test the database alteration
|
||||||
project_state.apps.get_model("test_adflpd", "pony").objects.create(
|
project_state.apps.get_model("test_adflpd", "pony").objects.create(
|
||||||
|
@ -1234,8 +1234,8 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertEqual(operation.describe(), "Alter field pink on Pony")
|
self.assertEqual(operation.describe(), "Alter field pink on Pony")
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
operation.state_forwards("test_alfl", new_state)
|
operation.state_forwards("test_alfl", new_state)
|
||||||
self.assertIs(project_state.models["test_alfl", "pony"].get_field_by_name("pink").null, False)
|
self.assertIs(project_state.models['test_alfl', 'pony'].fields['pink'].null, False)
|
||||||
self.assertIs(new_state.models["test_alfl", "pony"].get_field_by_name("pink").null, True)
|
self.assertIs(new_state.models['test_alfl', 'pony'].fields['pink'].null, True)
|
||||||
# Test the database alteration
|
# Test the database alteration
|
||||||
self.assertColumnNotNull("test_alfl_pony", "pink")
|
self.assertColumnNotNull("test_alfl_pony", "pink")
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
|
@ -1260,8 +1260,14 @@ class OperationTests(OperationTestBase):
|
||||||
operation = migrations.AlterField("Pony", "id", models.IntegerField(primary_key=True))
|
operation = migrations.AlterField("Pony", "id", models.IntegerField(primary_key=True))
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
operation.state_forwards("test_alflpk", new_state)
|
operation.state_forwards("test_alflpk", new_state)
|
||||||
self.assertIsInstance(project_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.AutoField)
|
self.assertIsInstance(
|
||||||
self.assertIsInstance(new_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.IntegerField)
|
project_state.models['test_alflpk', 'pony'].fields['id'],
|
||||||
|
models.AutoField,
|
||||||
|
)
|
||||||
|
self.assertIsInstance(
|
||||||
|
new_state.models['test_alflpk', 'pony'].fields['id'],
|
||||||
|
models.IntegerField,
|
||||||
|
)
|
||||||
# Test the database alteration
|
# Test the database alteration
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
operation.database_forwards("test_alflpk", editor, project_state, new_state)
|
operation.database_forwards("test_alflpk", editor, project_state, new_state)
|
||||||
|
@ -1289,8 +1295,14 @@ class OperationTests(OperationTestBase):
|
||||||
operation = migrations.AlterField("Pony", "id", models.FloatField(primary_key=True))
|
operation = migrations.AlterField("Pony", "id", models.FloatField(primary_key=True))
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
operation.state_forwards("test_alflpkfk", new_state)
|
operation.state_forwards("test_alflpkfk", new_state)
|
||||||
self.assertIsInstance(project_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.AutoField)
|
self.assertIsInstance(
|
||||||
self.assertIsInstance(new_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.FloatField)
|
project_state.models['test_alflpkfk', 'pony'].fields['id'],
|
||||||
|
models.AutoField,
|
||||||
|
)
|
||||||
|
self.assertIsInstance(
|
||||||
|
new_state.models['test_alflpkfk', 'pony'].fields['id'],
|
||||||
|
models.FloatField,
|
||||||
|
)
|
||||||
|
|
||||||
def assertIdTypeEqualsFkType():
|
def assertIdTypeEqualsFkType():
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
|
@ -1479,8 +1491,8 @@ class OperationTests(OperationTestBase):
|
||||||
self.assertEqual(operation.describe(), "Rename field pink on Pony to blue")
|
self.assertEqual(operation.describe(), "Rename field pink on Pony to blue")
|
||||||
new_state = project_state.clone()
|
new_state = project_state.clone()
|
||||||
operation.state_forwards("test_rnfl", new_state)
|
operation.state_forwards("test_rnfl", new_state)
|
||||||
self.assertIn("blue", [n for n, f in new_state.models["test_rnfl", "pony"].fields])
|
self.assertIn("blue", new_state.models["test_rnfl", "pony"].fields)
|
||||||
self.assertNotIn("pink", [n for n, f in new_state.models["test_rnfl", "pony"].fields])
|
self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].fields)
|
||||||
# Make sure the unique_together has the renamed column too
|
# Make sure the unique_together has the renamed column too
|
||||||
self.assertIn("blue", new_state.models["test_rnfl", "pony"].options['unique_together'][0])
|
self.assertIn("blue", new_state.models["test_rnfl", "pony"].options['unique_together'][0])
|
||||||
self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].options['unique_together'][0])
|
self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].options['unique_together'][0])
|
||||||
|
@ -1536,19 +1548,19 @@ class OperationTests(OperationTestBase):
|
||||||
operation = migrations.RenameField('Model', 'field', 'renamed')
|
operation = migrations.RenameField('Model', 'field', 'renamed')
|
||||||
new_state = state.clone()
|
new_state = state.clone()
|
||||||
operation.state_forwards('app', new_state)
|
operation.state_forwards('app', new_state)
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].remote_field.field_name, 'renamed')
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fk'].remote_field.field_name, 'renamed')
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].from_fields, ['self'])
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fk'].from_fields, ['self'])
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].to_fields, ('renamed',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fk'].to_fields, ('renamed',))
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[2][1].from_fields, ('fk',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].from_fields, ('fk',))
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[2][1].to_fields, ('renamed',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].to_fields, ('renamed',))
|
||||||
operation = migrations.RenameField('OtherModel', 'fk', 'renamed_fk')
|
operation = migrations.RenameField('OtherModel', 'fk', 'renamed_fk')
|
||||||
new_state = state.clone()
|
new_state = state.clone()
|
||||||
operation.state_forwards('app', new_state)
|
operation.state_forwards('app', new_state)
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].remote_field.field_name, 'renamed')
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['renamed_fk'].remote_field.field_name, 'renamed')
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].from_fields, ('self',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['renamed_fk'].from_fields, ('self',))
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].to_fields, ('renamed',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['renamed_fk'].to_fields, ('renamed',))
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[2][1].from_fields, ('renamed_fk',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].from_fields, ('renamed_fk',))
|
||||||
self.assertEqual(new_state.models['app', 'othermodel'].fields[2][1].to_fields, ('renamed',))
|
self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].to_fields, ('renamed',))
|
||||||
|
|
||||||
def test_alter_unique_together(self):
|
def test_alter_unique_together(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -121,10 +121,10 @@ class StateTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual(author_state.app_label, "migrations")
|
self.assertEqual(author_state.app_label, "migrations")
|
||||||
self.assertEqual(author_state.name, "Author")
|
self.assertEqual(author_state.name, "Author")
|
||||||
self.assertEqual([x for x, y in author_state.fields], ["id", "name", "bio", "age"])
|
self.assertEqual(list(author_state.fields), ["id", "name", "bio", "age"])
|
||||||
self.assertEqual(author_state.fields[1][1].max_length, 255)
|
self.assertEqual(author_state.fields['name'].max_length, 255)
|
||||||
self.assertIs(author_state.fields[2][1].null, False)
|
self.assertIs(author_state.fields['bio'].null, False)
|
||||||
self.assertIs(author_state.fields[3][1].null, True)
|
self.assertIs(author_state.fields['age'].null, True)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
author_state.options,
|
author_state.options,
|
||||||
{
|
{
|
||||||
|
@ -138,10 +138,10 @@ class StateTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual(book_state.app_label, "migrations")
|
self.assertEqual(book_state.app_label, "migrations")
|
||||||
self.assertEqual(book_state.name, "Book")
|
self.assertEqual(book_state.name, "Book")
|
||||||
self.assertEqual([x for x, y in book_state.fields], ["id", "title", "author", "contributors"])
|
self.assertEqual(list(book_state.fields), ["id", "title", "author", "contributors"])
|
||||||
self.assertEqual(book_state.fields[1][1].max_length, 1000)
|
self.assertEqual(book_state.fields['title'].max_length, 1000)
|
||||||
self.assertIs(book_state.fields[2][1].null, False)
|
self.assertIs(book_state.fields['author'].null, False)
|
||||||
self.assertEqual(book_state.fields[3][1].__class__.__name__, "ManyToManyField")
|
self.assertEqual(book_state.fields['contributors'].__class__.__name__, 'ManyToManyField')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
book_state.options,
|
book_state.options,
|
||||||
{"verbose_name": "tome", "db_table": "test_tome", "indexes": [book_index], "constraints": []},
|
{"verbose_name": "tome", "db_table": "test_tome", "indexes": [book_index], "constraints": []},
|
||||||
|
@ -150,7 +150,7 @@ class StateTests(SimpleTestCase):
|
||||||
|
|
||||||
self.assertEqual(author_proxy_state.app_label, "migrations")
|
self.assertEqual(author_proxy_state.app_label, "migrations")
|
||||||
self.assertEqual(author_proxy_state.name, "AuthorProxy")
|
self.assertEqual(author_proxy_state.name, "AuthorProxy")
|
||||||
self.assertEqual(author_proxy_state.fields, [])
|
self.assertEqual(author_proxy_state.fields, {})
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
author_proxy_state.options,
|
author_proxy_state.options,
|
||||||
{"proxy": True, "ordering": ["name"], "indexes": [], "constraints": []},
|
{"proxy": True, "ordering": ["name"], "indexes": [], "constraints": []},
|
||||||
|
@ -923,7 +923,7 @@ class StateTests(SimpleTestCase):
|
||||||
project_state.add_model(ModelState.from_model(Author))
|
project_state.add_model(ModelState.from_model(Author))
|
||||||
project_state.add_model(ModelState.from_model(Book))
|
project_state.add_model(ModelState.from_model(Book))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[name for name, field in project_state.models["migrations", "book"].fields],
|
list(project_state.models['migrations', 'book'].fields),
|
||||||
["id", "author"],
|
["id", "author"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1084,10 +1084,10 @@ class ModelStateTests(SimpleTestCase):
|
||||||
author_state = ModelState.from_model(Author)
|
author_state = ModelState.from_model(Author)
|
||||||
self.assertEqual(author_state.app_label, 'migrations')
|
self.assertEqual(author_state.app_label, 'migrations')
|
||||||
self.assertEqual(author_state.name, 'Author')
|
self.assertEqual(author_state.name, 'Author')
|
||||||
self.assertEqual([x for x, y in author_state.fields], ['id', 'name', 'bio', 'age'])
|
self.assertEqual(list(author_state.fields), ['id', 'name', 'bio', 'age'])
|
||||||
self.assertEqual(author_state.fields[1][1].max_length, 255)
|
self.assertEqual(author_state.fields['name'].max_length, 255)
|
||||||
self.assertIs(author_state.fields[2][1].null, False)
|
self.assertIs(author_state.fields['bio'].null, False)
|
||||||
self.assertIs(author_state.fields[3][1].null, True)
|
self.assertIs(author_state.fields['age'].null, True)
|
||||||
self.assertEqual(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': [], "constraints": []})
|
self.assertEqual(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': [], "constraints": []})
|
||||||
self.assertEqual(author_state.bases, (models.Model,))
|
self.assertEqual(author_state.bases, (models.Model,))
|
||||||
self.assertEqual(author_state.managers, [])
|
self.assertEqual(author_state.managers, [])
|
||||||
|
@ -1126,11 +1126,11 @@ class ModelStateTests(SimpleTestCase):
|
||||||
self.assertEqual(station_state.app_label, 'migrations')
|
self.assertEqual(station_state.app_label, 'migrations')
|
||||||
self.assertEqual(station_state.name, 'BusStation')
|
self.assertEqual(station_state.name, 'BusStation')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[x for x, y in station_state.fields],
|
list(station_state.fields),
|
||||||
['searchablelocation_ptr', 'name', 'bus_routes', 'inbound']
|
['searchablelocation_ptr', 'name', 'bus_routes', 'inbound']
|
||||||
)
|
)
|
||||||
self.assertEqual(station_state.fields[1][1].max_length, 128)
|
self.assertEqual(station_state.fields['name'].max_length, 128)
|
||||||
self.assertIs(station_state.fields[2][1].null, False)
|
self.assertIs(station_state.fields['bus_routes'].null, False)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
station_state.options,
|
station_state.options,
|
||||||
{'abstract': False, 'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': [], 'constraints': []}
|
{'abstract': False, 'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': [], 'constraints': []}
|
||||||
|
|
Loading…
Reference in New Issue