diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index f01c9feffd8..89b95b19a94 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -93,7 +93,7 @@ class MigrationAutodetector: of course, the related fields change during renames). """ fields_def = [] - for name, field in sorted(fields): + for name, field in sorted(fields.items()): deconstruction = self.deep_deconstruct(field) if field.remote_field and field.remote_field.model: del deconstruction[2]['to'] @@ -208,17 +208,17 @@ class MigrationAutodetector: self.kept_unmanaged_keys = self.old_unmanaged_keys & self.new_unmanaged_keys self.through_users = {} 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 x, y in self.from_state.models[ + for field_name in self.from_state.models[ app_label, self.renamed_models.get((app_label, model_name), model_name) ].fields } 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 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): @@ -226,7 +226,7 @@ class MigrationAutodetector: 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: + 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) if (hasattr(old_field, "remote_field") and getattr(old_field.remote_field, "through", None) and not old_field.remote_field.through._meta.auto_created): @@ -576,7 +576,7 @@ class MigrationAutodetector: app_label, operations.CreateModel( 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, bases=model_state.bases, managers=model_state.managers, @@ -820,7 +820,7 @@ class MigrationAutodetector: 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): 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) if field.remote_field and field.remote_field.model and 'to' in old_field_dec[2]: old_rel_to = old_field_dec[2]['to'] diff --git a/django/db/migrations/operations/fields.py b/django/db/migrations/operations/fields.py index ecb2af8a28a..e4f78e365eb 100644 --- a/django/db/migrations/operations/fields.py +++ b/django/db/migrations/operations/fields.py @@ -89,7 +89,7 @@ class AddField(FieldOperation): field.default = NOT_PROVIDED else: 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 = not field.is_relation 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): - new_fields = [] - old_field = None - 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 + model_state = state.models[app_label, self.model_name_lower] + old_field = model_state.fields.pop(self.name) # Delay rendering of relationships if it's not a relational field delay = not old_field.is_relation state.reload_model(app_label, self.model_name_lower, delay=delay) @@ -217,11 +211,8 @@ class AlterField(FieldOperation): field.default = NOT_PROVIDED else: field = self.field - state.models[app_label, self.model_name_lower].fields = [ - (n, field if n == self.name else f) - for n, f in - state.models[app_label, self.model_name_lower].fields - ] + model_state = state.models[app_label, self.model_name_lower] + model_state.fields[self.name] = field # TODO: investigate if old relational fields must be reloaded or if it's # sufficient if the new field is (#27737). # 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] # Rename the field fields = model_state.fields - found = None - for index, (name, field) in enumerate(fields): - if not found and name == self.old_name: - fields[index] = (self.new_name, field) - found = field + try: + found = fields.pop(self.old_name) + except KeyError: + raise FieldDoesNotExist( + "%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. from_fields = getattr(field, 'from_fields', None) if from_fields: @@ -311,10 +305,6 @@ class RenameField(FieldOperation): self.new_name if from_field_name == self.old_name else from_field_name 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 options = model_state.options for option in ('index_together', 'unique_together'): diff --git a/django/db/migrations/operations/models.py b/django/db/migrations/operations/models.py index baa902089f6..8c06c4d157b 100644 --- a/django/db/migrations/operations/models.py +++ b/django/db/migrations/operations/models.py @@ -310,7 +310,7 @@ class RenameModel(ModelOperation): old_model_tuple = (app_label, self.old_name_lower) new_remote_model = '%s.%s' % (app_label, self.new_name) 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 if reference.to: changed_field = field.clone() @@ -320,7 +320,7 @@ class RenameModel(ModelOperation): changed_field = field.clone() changed_field.remote_field.through = new_remote_model 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)) # Reload models related to old model before removing the old model. state.reload_models(to_reload, delay=True) diff --git a/django/db/migrations/operations/utils.py b/django/db/migrations/operations/utils.py index 0295d60af90..6e2bacd5496 100644 --- a/django/db/migrations/operations/utils.py +++ b/django/db/migrations/operations/utils.py @@ -83,17 +83,17 @@ def field_references( 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. If field_tuple is provided only references to this particular field of model_tuple will be generated. """ 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) if reference: - yield model_state, index, name, field, reference + yield model_state, name, field, reference def field_is_referenced(state, model_tuple, field_tuple): diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 9280f5cd26a..c8cb704aee1 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -125,7 +125,7 @@ class ProjectState: # Directly related models are the models pointed to by ForeignKeys, # OneToOneFields, and ManyToManyFields. direct_related_models = set() - for name, field in model_state.fields: + for field in model_state.fields.values(): if field.is_relation: if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT: continue @@ -359,16 +359,13 @@ class ModelState: def __init__(self, app_label, name, fields, options=None, bases=None, managers=None): self.app_label = app_label self.name = name - self.fields = fields + self.fields = dict(fields) self.options = options or {} self.options.setdefault('indexes', []) self.options.setdefault('constraints', []) self.bases = bases or (models.Model,) self.managers = managers or [] - # Sanity-check that fields is NOT a dict. It must be ordered. - 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: + for name, field in self.fields.items(): # Sanity-check that fields are NOT already bound to a model. if hasattr(field, 'model'): raise ValueError( @@ -544,7 +541,7 @@ class ModelState: return self.__class__( app_label=self.app_label, name=self.name, - fields=list(self.fields), + fields=dict(self.fields), # Since options are shallow-copied here, operations such as # AddIndex must replace their option (e.g 'indexes') rather # than mutating it. @@ -566,8 +563,8 @@ class ModelState: ) except LookupError: raise InvalidBasesError("Cannot resolve one or more bases from %r" % (self.bases,)) - # Turn fields into a dict for the body, add other bits - body = {name: field.clone() for name, field in self.fields} + # Clone fields for the body, add other bits. + body = {name: field.clone() for name, field in self.fields.items()} body['Meta'] = meta body['__module__'] = "__fake__" @@ -576,12 +573,6 @@ class ModelState: # Then, make a Model object (apps.register_model is called in __new__) 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): for index in self.options['indexes']: if index.name == name: @@ -602,8 +593,13 @@ class ModelState: (self.app_label == other.app_label) and (self.name == other.name) and (len(self.fields) == len(other.fields)) and - all((k1 == k2 and (f1.deconstruct()[1:] == f2.deconstruct()[1:])) - for (k1, f1), (k2, f2) in zip(sorted(self.fields), sorted(other.fields))) and + all( + 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.bases == other.bases) and (self.managers == other.managers) diff --git a/tests/migrations/test_executor.py b/tests/migrations/test_executor.py index 38391cdccf2..5a122bc6b6d 100644 --- a/tests/migrations/test_executor.py +++ b/tests/migrations/test_executor.py @@ -516,13 +516,13 @@ class ExecutorTests(MigrationTestBase): state = executor.migrate([ ('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() # Migrate backward. state = executor.migrate([ ('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([ ('mutate_state_b', None), ]) diff --git a/tests/migrations/test_loader.py b/tests/migrations/test_loader.py index 7bc9ccabd40..ce2f2091880 100644 --- a/tests/migrations/test_loader.py +++ b/tests/migrations/test_loader.py @@ -73,15 +73,12 @@ class LoaderTests(TestCase): author_state = project_state.models["migrations", "author"] self.assertEqual( - [x for x, y in author_state.fields], + list(author_state.fields), ["id", "name", "slug", "age", "rating"] ) book_state = project_state.models["migrations", "book"] - self.assertEqual( - [x for x, y in book_state.fields], - ["id", "author"] - ) + self.assertEqual(list(book_state.fields), ['id', 'author']) # Ensure we've included unmigrated apps in there too 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) book_state = project_state.models["migrations", "book"] - self.assertEqual( - [x for x, y in book_state.fields], - ["id", "user"] - ) + self.assertEqual(list(book_state.fields), ['id', 'user']) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_run_before"}) def test_run_before(self): diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index bb4b74e727a..55c20213b01 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -521,7 +521,10 @@ class OperationTests(OperationTestBase): self.assertNotIn(("test_rnmo", "pony"), new_state.models) self.assertIn(("test_rnmo", "horse"), new_state.models) # 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.assertTableExists("test_rnmo_horse") if connection.features.supports_foreign_keys: @@ -532,7 +535,10 @@ class OperationTests(OperationTestBase): # Test original state and database self.assertIn(("test_rnmo", "pony"), 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.assertTableNotExists("test_rnmo_horse") if connection.features.supports_foreign_keys: @@ -579,7 +585,7 @@ class OperationTests(OperationTestBase): # Remember, RenameModel also repoints all incoming FKs and M2Ms self.assertEqual( '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') 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) # RenameModel shouldn't repoint the superclass's relations, only local ones self.assertEqual( - project_state.models["test_rmwsc", "rider"].fields[1][1].remote_field.model, - new_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['pony'].remote_field.model, ) # Before running the migration we have a table for Shetland Pony, not Little Horse self.assertTableExists("test_rmwsc_shetlandpony") @@ -797,10 +803,7 @@ class OperationTests(OperationTestBase): self.assertEqual(operation.describe(), "Add field height to Pony") project_state, new_state = self.make_test_state("test_adfl", operation) self.assertEqual(len(new_state.models["test_adfl", "pony"].fields), 4) - field = [ - f for n, f in new_state.models["test_adfl", "pony"].fields - if n == "height" - ][0] + field = new_state.models['test_adfl', 'pony'].fields['height'] self.assertEqual(field.default, 5) # Test the database alteration self.assertColumnNotExists("test_adfl_pony", "height") @@ -974,10 +977,7 @@ class OperationTests(OperationTestBase): new_state = project_state.clone() operation.state_forwards("test_adflpd", new_state) self.assertEqual(len(new_state.models["test_adflpd", "pony"].fields), 4) - field = [ - f for n, f in new_state.models["test_adflpd", "pony"].fields - if n == "height" - ][0] + field = new_state.models['test_adflpd', 'pony'].fields['height'] self.assertEqual(field.default, models.NOT_PROVIDED) # Test the database alteration 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") new_state = project_state.clone() operation.state_forwards("test_alfl", new_state) - self.assertIs(project_state.models["test_alfl", "pony"].get_field_by_name("pink").null, False) - self.assertIs(new_state.models["test_alfl", "pony"].get_field_by_name("pink").null, True) + self.assertIs(project_state.models['test_alfl', 'pony'].fields['pink'].null, False) + self.assertIs(new_state.models['test_alfl', 'pony'].fields['pink'].null, True) # Test the database alteration self.assertColumnNotNull("test_alfl_pony", "pink") with connection.schema_editor() as editor: @@ -1260,8 +1260,14 @@ class OperationTests(OperationTestBase): operation = migrations.AlterField("Pony", "id", models.IntegerField(primary_key=True)) new_state = project_state.clone() operation.state_forwards("test_alflpk", new_state) - self.assertIsInstance(project_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.AutoField) - self.assertIsInstance(new_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.IntegerField) + self.assertIsInstance( + 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 with connection.schema_editor() as editor: 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)) new_state = project_state.clone() operation.state_forwards("test_alflpkfk", new_state) - self.assertIsInstance(project_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.AutoField) - self.assertIsInstance(new_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.FloatField) + self.assertIsInstance( + project_state.models['test_alflpkfk', 'pony'].fields['id'], + models.AutoField, + ) + self.assertIsInstance( + new_state.models['test_alflpkfk', 'pony'].fields['id'], + models.FloatField, + ) def assertIdTypeEqualsFkType(): with connection.cursor() as cursor: @@ -1479,8 +1491,8 @@ class OperationTests(OperationTestBase): self.assertEqual(operation.describe(), "Rename field pink on Pony to blue") new_state = project_state.clone() operation.state_forwards("test_rnfl", new_state) - self.assertIn("blue", [n for n, f in new_state.models["test_rnfl", "pony"].fields]) - self.assertNotIn("pink", [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", new_state.models["test_rnfl", "pony"].fields) # Make sure the unique_together has the renamed column too 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]) @@ -1536,19 +1548,19 @@ class OperationTests(OperationTestBase): operation = migrations.RenameField('Model', 'field', 'renamed') new_state = state.clone() 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[1][1].from_fields, ['self']) - self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].to_fields, ('renamed',)) - self.assertEqual(new_state.models['app', 'othermodel'].fields[2][1].from_fields, ('fk',)) - self.assertEqual(new_state.models['app', 'othermodel'].fields[2][1].to_fields, ('renamed',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['fk'].remote_field.field_name, 'renamed') + self.assertEqual(new_state.models['app', 'othermodel'].fields['fk'].from_fields, ['self']) + self.assertEqual(new_state.models['app', 'othermodel'].fields['fk'].to_fields, ('renamed',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].from_fields, ('fk',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].to_fields, ('renamed',)) operation = migrations.RenameField('OtherModel', 'fk', 'renamed_fk') new_state = state.clone() 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[1][1].from_fields, ('self',)) - self.assertEqual(new_state.models['app', 'othermodel'].fields[1][1].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[2][1].to_fields, ('renamed',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['renamed_fk'].remote_field.field_name, 'renamed') + self.assertEqual(new_state.models['app', 'othermodel'].fields['renamed_fk'].from_fields, ('self',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['renamed_fk'].to_fields, ('renamed',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].from_fields, ('renamed_fk',)) + self.assertEqual(new_state.models['app', 'othermodel'].fields['fo'].to_fields, ('renamed',)) def test_alter_unique_together(self): """ diff --git a/tests/migrations/test_state.py b/tests/migrations/test_state.py index 3b34376188a..40277bf506c 100644 --- a/tests/migrations/test_state.py +++ b/tests/migrations/test_state.py @@ -121,10 +121,10 @@ class StateTests(SimpleTestCase): self.assertEqual(author_state.app_label, "migrations") self.assertEqual(author_state.name, "Author") - self.assertEqual([x for x, y in author_state.fields], ["id", "name", "bio", "age"]) - self.assertEqual(author_state.fields[1][1].max_length, 255) - self.assertIs(author_state.fields[2][1].null, False) - self.assertIs(author_state.fields[3][1].null, True) + self.assertEqual(list(author_state.fields), ["id", "name", "bio", "age"]) + self.assertEqual(author_state.fields['name'].max_length, 255) + self.assertIs(author_state.fields['bio'].null, False) + self.assertIs(author_state.fields['age'].null, True) self.assertEqual( author_state.options, { @@ -138,10 +138,10 @@ class StateTests(SimpleTestCase): self.assertEqual(book_state.app_label, "migrations") self.assertEqual(book_state.name, "Book") - self.assertEqual([x for x, y in book_state.fields], ["id", "title", "author", "contributors"]) - self.assertEqual(book_state.fields[1][1].max_length, 1000) - self.assertIs(book_state.fields[2][1].null, False) - self.assertEqual(book_state.fields[3][1].__class__.__name__, "ManyToManyField") + self.assertEqual(list(book_state.fields), ["id", "title", "author", "contributors"]) + self.assertEqual(book_state.fields['title'].max_length, 1000) + self.assertIs(book_state.fields['author'].null, False) + self.assertEqual(book_state.fields['contributors'].__class__.__name__, 'ManyToManyField') self.assertEqual( book_state.options, {"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.name, "AuthorProxy") - self.assertEqual(author_proxy_state.fields, []) + self.assertEqual(author_proxy_state.fields, {}) self.assertEqual( author_proxy_state.options, {"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(Book)) self.assertEqual( - [name for name, field in project_state.models["migrations", "book"].fields], + list(project_state.models['migrations', 'book'].fields), ["id", "author"], ) @@ -1084,10 +1084,10 @@ class ModelStateTests(SimpleTestCase): author_state = ModelState.from_model(Author) self.assertEqual(author_state.app_label, 'migrations') self.assertEqual(author_state.name, 'Author') - self.assertEqual([x for x, y in author_state.fields], ['id', 'name', 'bio', 'age']) - self.assertEqual(author_state.fields[1][1].max_length, 255) - self.assertIs(author_state.fields[2][1].null, False) - self.assertIs(author_state.fields[3][1].null, True) + self.assertEqual(list(author_state.fields), ['id', 'name', 'bio', 'age']) + self.assertEqual(author_state.fields['name'].max_length, 255) + self.assertIs(author_state.fields['bio'].null, False) + self.assertIs(author_state.fields['age'].null, True) self.assertEqual(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': [], "constraints": []}) self.assertEqual(author_state.bases, (models.Model,)) self.assertEqual(author_state.managers, []) @@ -1126,11 +1126,11 @@ class ModelStateTests(SimpleTestCase): self.assertEqual(station_state.app_label, 'migrations') self.assertEqual(station_state.name, 'BusStation') self.assertEqual( - [x for x, y in station_state.fields], + list(station_state.fields), ['searchablelocation_ptr', 'name', 'bus_routes', 'inbound'] ) - self.assertEqual(station_state.fields[1][1].max_length, 128) - self.assertIs(station_state.fields[2][1].null, False) + self.assertEqual(station_state.fields['name'].max_length, 128) + self.assertIs(station_state.fields['bus_routes'].null, False) self.assertEqual( station_state.options, {'abstract': False, 'swappable': 'TEST_SWAPPABLE_MODEL', 'indexes': [], 'constraints': []}