tests state construction rendering and modification by operations tests making a projectstate from an apps the managers on this model should be ordered by their creation counter and not by the order in model body the default manager is used in migrations no explicit managers defined migrations will fall back to the default foodmgr is used in migration but isn t the default mgr hence add the default when the default manager of the model is a custom manager it needs to be added to the model state when a manager is added with a name of objects but it does not have useinmigrations true no migration should be added to the model state 26643 when a manager is added with useinmigrations true and a parent model had a manager with the same name and useinmigrations true the parent s manager shouldn t appear in the model state 26881 stateapps bulkupdate should update apps ready to false and reset the value afterward tests rendering a projectstate into an apps the ordering we really want is objects mgr1 mgr2 first test rendering individually we shouldn t be able to render yet once the parent model is in the app registry it should be fine we shouldn t be able to render yet once the parent models are in the app registry it should be fine the projectstate render method correctly renders models to account for intermodel base dependencies make a projectstate and render it now make an invalid projectstate and make sure it fails the projectstate render method doesn t raise an improperlyconfigured exception about unique labels if two dotted app names have the same last part make a projectstate and render it the model is reloaded even on changes that are not involved in relations other models pointing to or from it are also reloaded 24573 adding relations to existing models should reload the referenced models too the relations between the old models are correct all models have changed the relations between the old models still hold the relations between the new models correct 24225 relations between models are updated while remaining the relations and references for models of an old state model from oldstate still has the relation same test for deleted model 24513 modifying an object pointing to itself would cause it to be rendered twice and thus breaking its related m2m through objects at this point the model would be rendered twice causing its related m2m through objects to point to an old copy and thus breaking their attribute lookup the old model s meta is still consistent the new model s meta is still consistent and are implemented correctly test two things that should be equal make a very small change maxlen 99 and see if that affects it make a valid projectstate and render it now make an invalid one with a foreignkey and another with manytomanyfield and now with multiple models and multiple fields including real apps can resolve dangling fk errors this test relies on the fact that contenttypes is always loaded if we just stick it into an empty state it should fail if we include the real app it should succeed makes sure projectstate doesn t include orderwrt fields when making from existing models make a valid projectstate and render it 24147 managers refer to the correct version of a historical model 24483 projectstate fromapps should not destructively consume field choices iterators projectstate s relations are populated on relations access rename a model without relations add a selfreferential foreign key add a foreign key add a manytomany field with the through model remove a manytomany field remove a foreign key remove a nonrelation field rename a nonrelation field alter a foreign key to a nonrelation field alter a nonrelation field to a manytomany field alter a manytomany field to a foreign key two foreign keys to the same model rename the second foreign key remove the first foreign key rendering a model state doesn t alter its internal fields purposely reordered tests making a projectstate from an apps with a swappable model a swappable model inheriting from a hierarchy concrete abstract concrete tests making a projectstate from unused models with custom managers the default manager is used in migrations modifying the state doesn t modify the index on the model m has a pointer o2o field pptr to p tests state construction rendering and modification by operations tests making a projectstate from an apps the managers on this model should be ordered by their creation counter and not by the order in model body the default manager is used in migrations no explicit managers defined migrations will fall back to the default food_mgr is used in migration but isn t the default mgr hence add the default when the default manager of the model is a custom manager it needs to be added to the model state when a manager is added with a name of objects but it does not have use_in_migrations true no migration should be added to the model state 26643 when a manager is added with use_in_migrations true and a parent model had a manager with the same name and use_in_migrations true the parent s manager shouldn t appear in the model state 26881 stateapps bulk_update should update apps ready to false and reset the value afterward tests rendering a projectstate into an apps the ordering we really want is objects mgr1 mgr2 first test rendering individually we shouldn t be able to render yet once the parent model is in the app registry it should be fine we shouldn t be able to render yet once the parent models are in the app registry it should be fine the projectstate render method correctly renders models to account for inter model base dependencies make a projectstate and render it now make an invalid projectstate and make sure it fails the projectstate render method doesn t raise an improperlyconfigured exception about unique labels if two dotted app names have the same last part make a projectstate and render it the model is reloaded even on changes that are not involved in relations other models pointing to or from it are also reloaded render project state 24573 adding relations to existing models should reload the referenced models too we need to work with rendered models the relations between the old models are correct all models have changed the relations between the old models still hold the relations between the new models correct 24225 relations between models are updated while remaining the relations and references for models of an old state model from old_state still has the relation same test for deleted model 24513 modifying an object pointing to itself would cause it to be rendered twice and thus breaking its related m2m through objects at this point the model would be rendered twice causing its related m2m through objects to point to an old copy and thus breaking their attribute lookup the old model s _meta is still consistent the new model s _meta is still consistent and are implemented correctly test two things that should be equal fill the apps cached property make a very small change max_len 99 and see if that affects it make a valid projectstate and render it now make an invalid one with a foreignkey and another with manytomanyfield and now with multiple models and multiple fields including real apps can resolve dangling fk errors this test relies on the fact that contenttypes is always loaded if we just stick it into an empty state it should fail if we include the real app it should succeed makes sure projectstate doesn t include orderwrt fields when making from existing models make a valid projectstate and render it 24147 managers refer to the correct version of a historical model 24483 projectstate from_apps should not destructively consume field choices iterators projectstate s _relations are populated on relations access rename a model without relations add a self referential foreign key add a foreign key add a many to many field with the through model remove a many to many field remove a foreign key remove a non relation field rename a non relation field alter a foreign key to a non relation field alter a non relation field to a many to many field alter a many to many field to a foreign key two foreign keys to the same model rename the second foreign key remove the first foreign key rendering a model state doesn t alter its internal fields purposely re ordered tests making a projectstate from an apps with a swappable model a swappable model inheriting from a hierarchy concrete abstract concrete tests making a projectstate from unused models with custom managers the default manager is used in migrations modifying the state doesn t modify the index on the model m has a pointer o2o field p_ptr to p
from django.apps.registry import Apps
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models
from django.db.migrations.exceptions import InvalidBasesError
from django.db.migrations.operations import (
    AddField,
    AlterField,
    DeleteModel,
    RemoveField,
)
from django.db.migrations.state import (
    ModelState,
    ProjectState,
    get_related_models_recursive,
)
from django.test import SimpleTestCase, override_settings
from django.test.utils import isolate_apps

from .models import (
    FoodManager,
    FoodQuerySet,
    ModelWithCustomBase,
    NoMigrationFoodManager,
    UnicodeModel,
)


class StateTests(SimpleTestCase):
    

    def test_create(self):
        

        new_apps = Apps(["migrations"])

        class Author(models.Model):
            name = models.CharField(max_length=255)
            bio = models.TextField()
            age = models.IntegerField(blank=True, null=True)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                unique_together = ["name", "bio"]

        class AuthorProxy(Author):
            class Meta:
                app_label = "migrations"
                apps = new_apps
                proxy = True
                ordering = ["name"]

        class SubAuthor(Author):
            width = models.FloatField(null=True)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            title = models.CharField(max_length=1000)
            author = models.ForeignKey(Author, models.CASCADE)
            contributors = models.ManyToManyField(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                verbose_name = "tome"
                db_table = "test_tome"
                indexes = [models.Index(fields=["title"])]

        class Food(models.Model):
            food_mgr = FoodManager("a", "b")
            food_qs = FoodQuerySet.as_manager()
            food_no_mgr = NoMigrationFoodManager("x", "y")

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class FoodNoManagers(models.Model):
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class FoodNoDefaultManager(models.Model):
            food_no_mgr = NoMigrationFoodManager("x", "y")
            food_mgr = FoodManager("a", "b")
            food_qs = FoodQuerySet.as_manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        mgr1 = FoodManager("a", "b")
        mgr2 = FoodManager("x", "y", c=3, d=4)

        class FoodOrderedManagers(models.Model):
            
            

            food_no_mgr = NoMigrationFoodManager("x", "y")
            food_mgr2 = mgr2
            food_mgr1 = mgr1

            class Meta:
                app_label = "migrations"
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models["migrations", "author"]
        author_proxy_state = project_state.models["migrations", "authorproxy"]
        sub_author_state = project_state.models["migrations", "subauthor"]
        book_state = project_state.models["migrations", "book"]
        food_state = project_state.models["migrations", "food"]
        food_no_managers_state = project_state.models["migrations", "foodnomanagers"]
        food_no_default_manager_state = project_state.models[
            "migrations", "foodnodefaultmanager"
        ]
        food_order_manager_state = project_state.models[
            "migrations", "foodorderedmanagers"
        ]
        book_index = models.Index(fields=["title"])
        book_index.set_name_with_model(Book)

        self.assertEqual(author_state.app_label, "migrations")
        self.assertEqual(author_state.name, "Author")
        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,
            {
                "unique_together": {("name", "bio")},
                "indexes": [],
                "constraints": [],
            },
        )
        self.assertEqual(author_state.bases, (models.Model,))

        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        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": [],
            },
        )
        self.assertEqual(book_state.bases, (models.Model,))

        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.options,
            {"proxy": True, "ordering": ["name"], "indexes": [], "constraints": []},
        )
        self.assertEqual(author_proxy_state.bases, ("migrations.author",))

        self.assertEqual(sub_author_state.app_label, "migrations")
        self.assertEqual(sub_author_state.name, "SubAuthor")
        self.assertEqual(len(sub_author_state.fields), 2)
        self.assertEqual(sub_author_state.bases, ("migrations.author",))

        
        self.assertEqual([name for name, mgr in food_state.managers], ["food_mgr"])
        self.assertTrue(all(isinstance(name, str) for name, mgr in food_state.managers))
        self.assertEqual(food_state.managers[0][1].args, ("a", "b", 1, 2))

        
        self.assertEqual(food_no_managers_state.managers, [])

        
        
        self.assertEqual(
            [name for name, mgr in food_no_default_manager_state.managers],
            ["food_no_mgr", "food_mgr"],
        )
        self.assertTrue(
            all(
                isinstance(name, str)
                for name, mgr in food_no_default_manager_state.managers
            )
        )
        self.assertEqual(
            food_no_default_manager_state.managers[0][1].__class__, models.Manager
        )
        self.assertIsInstance(food_no_default_manager_state.managers[1][1], FoodManager)

        self.assertEqual(
            [name for name, mgr in food_order_manager_state.managers],
            ["food_mgr1", "food_mgr2"],
        )
        self.assertTrue(
            all(
                isinstance(name, str) for name, mgr in food_order_manager_state.managers
            )
        )
        self.assertEqual(
            [mgr.args for name, mgr in food_order_manager_state.managers],
            [("a", "b", 1, 2), ("x", "y", 3, 4)],
        )

    def test_custom_default_manager_added_to_the_model_state(self):
        
        new_apps = Apps(["migrations"])
        custom_manager = models.Manager()

        class Author(models.Model):
            objects = models.TextField()
            authors = custom_manager

            class Meta:
                app_label = "migrations"
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models["migrations", "author"]
        self.assertEqual(author_state.managers, [("authors", custom_manager)])

    def test_custom_default_manager_named_objects_with_false_migration_flag(self):
        
        new_apps = Apps(["migrations"])

        class Author(models.Model):
            objects = models.Manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models["migrations", "author"]
        self.assertEqual(author_state.managers, [])

    def test_no_duplicate_managers(self):
        
        new_apps = Apps(["migrations"])

        class PersonManager(models.Manager):
            use_in_migrations = True

        class Person(models.Model):
            objects = PersonManager()

            class Meta:
                abstract = True

        class BossManager(PersonManager):
            use_in_migrations = True

        class Boss(Person):
            objects = BossManager()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        boss_state = project_state.models["migrations", "boss"]
        self.assertEqual(boss_state.managers, [("objects", Boss.objects)])

    def test_custom_default_manager(self):
        new_apps = Apps(["migrations"])

        class Author(models.Model):
            manager1 = models.Manager()
            manager2 = models.Manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps
                default_manager_name = "manager2"

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models["migrations", "author"]
        self.assertEqual(author_state.options["default_manager_name"], "manager2")
        self.assertEqual(author_state.managers, [("manager2", Author.manager1)])

    def test_custom_base_manager(self):
        new_apps = Apps(["migrations"])

        class Author(models.Model):
            manager1 = models.Manager()
            manager2 = models.Manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps
                base_manager_name = "manager2"

        class Author2(models.Model):
            manager1 = models.Manager()
            manager2 = models.Manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps
                base_manager_name = "manager1"

        project_state = ProjectState.from_apps(new_apps)

        author_state = project_state.models["migrations", "author"]
        self.assertEqual(author_state.options["base_manager_name"], "manager2")
        self.assertEqual(
            author_state.managers,
            [
                ("manager1", Author.manager1),
                ("manager2", Author.manager2),
            ],
        )

        author2_state = project_state.models["migrations", "author2"]
        self.assertEqual(author2_state.options["base_manager_name"], "manager1")
        self.assertEqual(
            author2_state.managers,
            [
                ("manager1", Author2.manager1),
            ],
        )

    def test_apps_bulk_update(self):
        
        project_state = ProjectState()
        apps = project_state.apps
        with apps.bulk_update():
            self.assertFalse(apps.ready)
        self.assertTrue(apps.ready)
        with self.assertRaises(ValueError):
            with apps.bulk_update():
                self.assertFalse(apps.ready)
                raise ValueError()
        self.assertTrue(apps.ready)

    def test_render(self):
        
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="Tag",
                fields=[
                    ("id", models.AutoField(primary_key=True)),
                    ("name", models.CharField(max_length=100)),
                    ("hidden", models.BooleanField()),
                ],
            )
        )
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="SubTag",
                fields=[
                    (
                        "tag_ptr",
                        models.OneToOneField(
                            "migrations.Tag",
                            models.CASCADE,
                            auto_created=True,
                            parent_link=True,
                            primary_key=True,
                            to_field="id",
                            serialize=False,
                        ),
                    ),
                    ("awesome", models.BooleanField()),
                ],
                bases=("migrations.Tag",),
            )
        )

        base_mgr = models.Manager()
        mgr1 = FoodManager("a", "b")
        mgr2 = FoodManager("x", "y", c=3, d=4)
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="Food",
                fields=[
                    ("id", models.AutoField(primary_key=True)),
                ],
                managers=[
                    
                    ("default", base_mgr),
                    ("food_mgr2", mgr2),
                    ("food_mgr1", mgr1),
                ],
            )
        )

        new_apps = project_state.apps
        self.assertEqual(
            new_apps.get_model("migrations", "Tag")._meta.get_field("name").max_length,
            100,
        )
        self.assertIs(
            new_apps.get_model("migrations", "Tag")._meta.get_field("hidden").null,
            False,
        )

        self.assertEqual(
            len(new_apps.get_model("migrations", "SubTag")._meta.local_fields), 2
        )

        Food = new_apps.get_model("migrations", "Food")
        self.assertEqual(
            [mgr.name for mgr in Food._meta.managers],
            ["default", "food_mgr1", "food_mgr2"],
        )
        self.assertTrue(all(isinstance(mgr.name, str) for mgr in Food._meta.managers))
        self.assertEqual(
            [mgr.__class__ for mgr in Food._meta.managers],
            [models.Manager, FoodManager, FoodManager],
        )

    def test_render_model_inheritance(self):
        class Book(models.Model):
            title = models.CharField(max_length=1000)

            class Meta:
                app_label = "migrations"
                apps = Apps()

        class Novel(Book):
            class Meta:
                app_label = "migrations"
                apps = Apps()

        
        apps = Apps(["migrations"])

        
        ms = ModelState.from_model(Novel)
        with self.assertRaises(InvalidBasesError):
            ms.render(apps)

        
        ModelState.from_model(Book).render(apps)
        ModelState.from_model(Novel).render(apps)

    def test_render_model_with_multiple_inheritance(self):
        class Foo(models.Model):
            class Meta:
                app_label = "migrations"
                apps = Apps()

        class Bar(models.Model):
            class Meta:
                app_label = "migrations"
                apps = Apps()

        class FooBar(Foo, Bar):
            class Meta:
                app_label = "migrations"
                apps = Apps()

        class AbstractSubFooBar(FooBar):
            class Meta:
                abstract = True
                apps = Apps()

        class SubFooBar(AbstractSubFooBar):
            class Meta:
                app_label = "migrations"
                apps = Apps()

        apps = Apps(["migrations"])

        
        ms = ModelState.from_model(FooBar)
        with self.assertRaises(InvalidBasesError):
            ms.render(apps)

        
        ModelState.from_model(Foo).render(apps)
        self.assertSequenceEqual(ModelState.from_model(Foo).bases, [models.Model])
        ModelState.from_model(Bar).render(apps)
        self.assertSequenceEqual(ModelState.from_model(Bar).bases, [models.Model])
        ModelState.from_model(FooBar).render(apps)
        self.assertSequenceEqual(
            ModelState.from_model(FooBar).bases, ["migrations.foo", "migrations.bar"]
        )
        ModelState.from_model(SubFooBar).render(apps)
        self.assertSequenceEqual(
            ModelState.from_model(SubFooBar).bases, ["migrations.foobar"]
        )

    def test_render_project_dependencies(self):
        
        new_apps = Apps()

        class A(models.Model):
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class B(A):
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class C(B):
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class D(A):
            class Meta:
                app_label = "migrations"
                apps = new_apps

        class E(B):
            class Meta:
                app_label = "migrations"
                apps = new_apps
                proxy = True

        class F(D):
            class Meta:
                app_label = "migrations"
                apps = new_apps
                proxy = True

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        project_state.add_model(ModelState.from_model(C))
        project_state.add_model(ModelState.from_model(D))
        project_state.add_model(ModelState.from_model(E))
        project_state.add_model(ModelState.from_model(F))
        final_apps = project_state.apps
        self.assertEqual(len(final_apps.get_models()), 6)

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        project_state.add_model(ModelState.from_model(C))
        project_state.add_model(ModelState.from_model(F))
        with self.assertRaises(InvalidBasesError):
            project_state.apps

    def test_render_unique_app_labels(self):
        

        class A(models.Model):
            class Meta:
                app_label = "django.contrib.auth"

        class B(models.Model):
            class Meta:
                app_label = "vendor.auth"

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        self.assertEqual(len(project_state.apps.get_models()), 2)

    def test_reload_related_model_on_non_relational_fields(self):
        
        project_state = ProjectState()
        project_state.apps  
        project_state.add_model(ModelState("migrations", "A", []))
        project_state.add_model(
            ModelState(
                "migrations",
                "B",
                [
                    ("a", models.ForeignKey("A", models.CASCADE)),
                ],
            )
        )
        project_state.add_model(
            ModelState(
                "migrations",
                "C",
                [
                    ("b", models.ForeignKey("B", models.CASCADE)),
                    ("name", models.TextField()),
                ],
            )
        )
        project_state.add_model(
            ModelState(
                "migrations",
                "D",
                [
                    ("a", models.ForeignKey("A", models.CASCADE)),
                ],
            )
        )
        operation = AlterField(
            model_name="C",
            name="name",
            field=models.TextField(blank=True),
        )
        operation.state_forwards("migrations", project_state)
        project_state.reload_model("migrations", "a", delay=True)
        A = project_state.apps.get_model("migrations.A")
        B = project_state.apps.get_model("migrations.B")
        D = project_state.apps.get_model("migrations.D")
        self.assertIs(B._meta.get_field("a").related_model, A)
        self.assertIs(D._meta.get_field("a").related_model, A)

    def test_reload_model_relationship_consistency(self):
        project_state = ProjectState()
        project_state.add_model(ModelState("migrations", "A", []))
        project_state.add_model(
            ModelState(
                "migrations",
                "B",
                [
                    ("a", models.ForeignKey("A", models.CASCADE)),
                ],
            )
        )
        project_state.add_model(
            ModelState(
                "migrations",
                "C",
                [
                    ("b", models.ForeignKey("B", models.CASCADE)),
                ],
            )
        )
        A = project_state.apps.get_model("migrations.A")
        B = project_state.apps.get_model("migrations.B")
        C = project_state.apps.get_model("migrations.C")
        self.assertEqual([r.related_model for r in A._meta.related_objects], [B])
        self.assertEqual([r.related_model for r in B._meta.related_objects], [C])
        self.assertEqual([r.related_model for r in C._meta.related_objects], [])

        project_state.reload_model("migrations", "a", delay=True)
        A = project_state.apps.get_model("migrations.A")
        B = project_state.apps.get_model("migrations.B")
        C = project_state.apps.get_model("migrations.C")
        self.assertEqual([r.related_model for r in A._meta.related_objects], [B])
        self.assertEqual([r.related_model for r in B._meta.related_objects], [C])
        self.assertEqual([r.related_model for r in C._meta.related_objects], [])

    def test_add_relations(self):
        
        new_apps = Apps()

        class A(models.Model):
            class Meta:
                app_label = "something"
                apps = new_apps

        class B(A):
            class Meta:
                app_label = "something"
                apps = new_apps

        class C(models.Model):
            class Meta:
                app_label = "something"
                apps = new_apps

        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        project_state.add_model(ModelState.from_model(C))

        project_state.apps  

        old_state = project_state.clone()
        model_a_old = old_state.apps.get_model("something", "A")
        model_b_old = old_state.apps.get_model("something", "B")
        model_c_old = old_state.apps.get_model("something", "C")
        
        self.assertIs(model_a_old._meta.get_field("b").related_model, model_b_old)
        self.assertIs(model_b_old._meta.get_field("a_ptr").related_model, model_a_old)

        operation = AddField(
            "c",
            "to_a",
            models.OneToOneField(
                "something.A",
                models.CASCADE,
                related_name="from_c",
            ),
        )
        operation.state_forwards("something", project_state)
        model_a_new = project_state.apps.get_model("something", "A")
        model_b_new = project_state.apps.get_model("something", "B")
        model_c_new = project_state.apps.get_model("something", "C")

        
        self.assertIsNot(model_a_old, model_a_new)
        self.assertIsNot(model_b_old, model_b_new)
        self.assertIsNot(model_c_old, model_c_new)
        
        self.assertIs(model_a_old._meta.get_field("b").related_model, model_b_old)
        self.assertIs(model_b_old._meta.get_field("a_ptr").related_model, model_a_old)
        
        self.assertIs(model_a_new._meta.get_field("b").related_model, model_b_new)
        self.assertIs(model_b_new._meta.get_field("a_ptr").related_model, model_a_new)
        self.assertIs(model_a_new._meta.get_field("from_c").related_model, model_c_new)
        self.assertIs(model_c_new._meta.get_field("to_a").related_model, model_a_new)

    def test_remove_relations(self):
        
        new_apps = Apps()

        class A(models.Model):
            class Meta:
                app_label = "something"
                apps = new_apps

        class B(models.Model):
            to_a = models.ForeignKey(A, models.CASCADE)

            class Meta:
                app_label = "something"
                apps = new_apps

        def get_model_a(state):
            return [
                mod for mod in state.apps.get_models() if mod._meta.model_name == "a"
            ][0]

        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1)
        old_state = project_state.clone()

        operation = RemoveField("b", "to_a")
        operation.state_forwards("something", project_state)
        
        model_a_old = get_model_a(old_state)
        model_a_new = get_model_a(project_state)
        self.assertIsNot(model_a_old, model_a_new)
        self.assertEqual(len(model_a_old._meta.related_objects), 1)
        self.assertEqual(len(model_a_new._meta.related_objects), 0)

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        project_state.add_model(ModelState.from_model(B))
        old_state = project_state.clone()

        operation = DeleteModel("b")
        operation.state_forwards("something", project_state)
        model_a_old = get_model_a(old_state)
        model_a_new = get_model_a(project_state)
        self.assertIsNot(model_a_old, model_a_new)
        self.assertEqual(len(model_a_old._meta.related_objects), 1)
        self.assertEqual(len(model_a_new._meta.related_objects), 0)

    def test_self_relation(self):
        

        class A(models.Model):
            to_a = models.ManyToManyField("something.A", symmetrical=False)

            class Meta:
                app_label = "something"

        def get_model_a(state):
            return [
                mod for mod in state.apps.get_models() if mod._meta.model_name == "a"
            ][0]

        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(A))
        self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1)
        old_state = project_state.clone()

        operation = AlterField(
            model_name="a",
            name="to_a",
            field=models.ManyToManyField("something.A", symmetrical=False, blank=True),
        )
        
        
        
        operation.state_forwards("something", project_state)

        model_a_old = get_model_a(old_state)
        model_a_new = get_model_a(project_state)
        self.assertIsNot(model_a_old, model_a_new)

        
        field_to_a_old = model_a_old._meta.get_field("to_a")
        self.assertEqual(field_to_a_old.m2m_field_name(), "from_a")
        self.assertEqual(field_to_a_old.m2m_reverse_field_name(), "to_a")
        self.assertIs(field_to_a_old.related_model, model_a_old)
        self.assertIs(
            field_to_a_old.remote_field.through._meta.get_field("to_a").related_model,
            model_a_old,
        )
        self.assertIs(
            field_to_a_old.remote_field.through._meta.get_field("from_a").related_model,
            model_a_old,
        )

        
        field_to_a_new = model_a_new._meta.get_field("to_a")
        self.assertEqual(field_to_a_new.m2m_field_name(), "from_a")
        self.assertEqual(field_to_a_new.m2m_reverse_field_name(), "to_a")
        self.assertIs(field_to_a_new.related_model, model_a_new)
        self.assertIs(
            field_to_a_new.remote_field.through._meta.get_field("to_a").related_model,
            model_a_new,
        )
        self.assertIs(
            field_to_a_new.remote_field.through._meta.get_field("from_a").related_model,
            model_a_new,
        )

    def test_equality(self):
        
        
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                "migrations",
                "Tag",
                [
                    ("id", models.AutoField(primary_key=True)),
                    ("name", models.CharField(max_length=100)),
                    ("hidden", models.BooleanField()),
                ],
                {},
                None,
            )
        )
        project_state.apps  
        other_state = project_state.clone()
        self.assertEqual(project_state, project_state)
        self.assertEqual(project_state, other_state)
        self.assertIs(project_state != project_state, False)
        self.assertIs(project_state != other_state, False)
        self.assertNotEqual(project_state.apps, other_state.apps)

        
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                "migrations",
                "Tag",
                [
                    ("id", models.AutoField(primary_key=True)),
                    ("name", models.CharField(max_length=99)),
                    ("hidden", models.BooleanField()),
                ],
                {},
                None,
            )
        )
        self.assertNotEqual(project_state, other_state)
        self.assertIs(project_state == other_state, False)

    def test_dangling_references_throw_error(self):
        new_apps = Apps()

        class Author(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Publisher(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            author = models.ForeignKey(Author, models.CASCADE)
            publisher = models.ForeignKey(Publisher, models.CASCADE)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Magazine(models.Model):
            authors = models.ManyToManyField(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Author))
        project_state.add_model(ModelState.from_model(Publisher))
        project_state.add_model(ModelState.from_model(Book))
        project_state.add_model(ModelState.from_model(Magazine))
        self.assertEqual(len(project_state.apps.get_models()), 4)

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "The field migrations.Book.author was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model "
            "'author'.\n"
            "The field migrations.Book.publisher was declared with a lazy reference "
            "to 'migrations.publisher', but app 'migrations' doesn't provide model "
            "'publisher'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Magazine))
        msg = (
            "The field migrations.Magazine.authors was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model "
            "'author'.\n"
            "The field migrations.Magazine_authors.author was declared with a lazy "
            "reference to 'migrations.author', but app 'migrations' doesn't provide "
            "model 'author'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "The field migrations.Book.author was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model "
            "'author'.\n"
            "The field migrations.Book.publisher was declared with a lazy reference "
            "to 'migrations.publisher', but app 'migrations' doesn't provide model "
            "'publisher'.\n"
            "The field migrations.Magazine.authors was declared with a lazy reference "
            "to 'migrations.author', but app 'migrations' doesn't provide model "
            "'author'.\n"
            "The field migrations.Magazine_authors.author was declared with a lazy "
            "reference to 'migrations.author', but app 'migrations' doesn't provide "
            "model 'author'."
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

    def test_reference_mixed_case_app_label(self):
        new_apps = Apps()

        class Author(models.Model):
            class Meta:
                app_label = "MiXedCase_migrations"
                apps = new_apps

        class Book(models.Model):
            author = models.ForeignKey(Author, models.CASCADE)

            class Meta:
                app_label = "MiXedCase_migrations"
                apps = new_apps

        class Magazine(models.Model):
            authors = models.ManyToManyField(Author)

            class Meta:
                app_label = "MiXedCase_migrations"
                apps = new_apps

        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Author))
        project_state.add_model(ModelState.from_model(Book))
        project_state.add_model(ModelState.from_model(Magazine))
        self.assertEqual(len(project_state.apps.get_models()), 3)

    def test_real_apps(self):
        
        new_apps = Apps()

        class TestModel(models.Model):
            ct = models.ForeignKey("contenttypes.ContentType", models.CASCADE)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(TestModel))
        with self.assertRaises(ValueError):
            project_state.apps

        
        project_state = ProjectState(real_apps={"contenttypes"})
        project_state.add_model(ModelState.from_model(TestModel))
        rendered_state = project_state.apps
        self.assertEqual(
            len(
                [
                    x
                    for x in rendered_state.get_models()
                    if x._meta.app_label == "migrations"
                ]
            ),
            1,
        )

    def test_real_apps_non_set(self):
        with self.assertRaises(AssertionError):
            ProjectState(real_apps=["contenttypes"])

    def test_ignore_order_wrt(self):
        
        new_apps = Apps()

        class Author(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            author = models.ForeignKey(Author, models.CASCADE)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                order_with_respect_to = "author"

        
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Author))
        project_state.add_model(ModelState.from_model(Book))
        self.assertEqual(
            list(project_state.models["migrations", "book"].fields),
            ["id", "author"],
        )

    def test_modelstate_get_field_order_wrt(self):
        new_apps = Apps()

        class Author(models.Model):
            name = models.TextField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Book(models.Model):
            author = models.ForeignKey(Author, models.CASCADE)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                order_with_respect_to = "author"

        model_state = ModelState.from_model(Book)
        order_wrt_field = model_state.get_field("_order")
        self.assertIsInstance(order_wrt_field, models.ForeignKey)
        self.assertEqual(order_wrt_field.related_model, "migrations.author")

    def test_modelstate_get_field_no_order_wrt_order_field(self):
        new_apps = Apps()

        class HistoricalRecord(models.Model):
            _order = models.PositiveSmallIntegerField()

            class Meta:
                app_label = "migrations"
                apps = new_apps

        model_state = ModelState.from_model(HistoricalRecord)
        order_field = model_state.get_field("_order")
        self.assertIsNone(order_field.related_model)
        self.assertIsInstance(order_field, models.PositiveSmallIntegerField)

    def test_manager_refer_correct_model_version(self):
        
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="Tag",
                fields=[
                    ("id", models.AutoField(primary_key=True)),
                    ("hidden", models.BooleanField()),
                ],
                managers=[
                    ("food_mgr", FoodManager("a", "b")),
                    ("food_qs", FoodQuerySet.as_manager()),
                ],
            )
        )

        old_model = project_state.apps.get_model("migrations", "tag")

        new_state = project_state.clone()
        operation = RemoveField("tag", "hidden")
        operation.state_forwards("migrations", new_state)

        new_model = new_state.apps.get_model("migrations", "tag")

        self.assertIsNot(old_model, new_model)
        self.assertIs(old_model, old_model.food_mgr.model)
        self.assertIs(old_model, old_model.food_qs.model)
        self.assertIs(new_model, new_model.food_mgr.model)
        self.assertIs(new_model, new_model.food_qs.model)
        self.assertIsNot(old_model.food_mgr, new_model.food_mgr)
        self.assertIsNot(old_model.food_qs, new_model.food_qs)
        self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model)
        self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model)

    def test_choices_iterator(self):
        
        new_apps = Apps(["migrations"])
        choices = [("a", "A"), ("b", "B")]

        class Author(models.Model):
            name = models.CharField(max_length=255)
            choice = models.CharField(max_length=255, choices=iter(choices))

            class Meta:
                app_label = "migrations"
                apps = new_apps

        ProjectState.from_apps(new_apps)
        choices_field = Author._meta.get_field("choice")
        self.assertEqual(list(choices_field.choices), choices)


class StateRelationsTests(SimpleTestCase):
    def get_base_project_state(self):
        new_apps = Apps()

        class User(models.Model):
            class Meta:
                app_label = "tests"
                apps = new_apps

        class Comment(models.Model):
            text = models.TextField()
            user = models.ForeignKey(User, models.CASCADE)
            comments = models.ManyToManyField("self")

            class Meta:
                app_label = "tests"
                apps = new_apps

        class Post(models.Model):
            text = models.TextField()
            authors = models.ManyToManyField(User)

            class Meta:
                app_label = "tests"
                apps = new_apps

        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(User))
        project_state.add_model(ModelState.from_model(Comment))
        project_state.add_model(ModelState.from_model(Post))
        return project_state

    def test_relations_population(self):
        tests = [
            (
                "add_model",
                [
                    ModelState(
                        app_label="migrations",
                        name="Tag",
                        fields=[("id", models.AutoField(primary_key=True))],
                    ),
                ],
            ),
            ("remove_model", ["tests", "comment"]),
            ("rename_model", ["tests", "comment", "opinion"]),
            (
                "add_field",
                [
                    "tests",
                    "post",
                    "next_post",
                    models.ForeignKey("self", models.CASCADE),
                    True,
                ],
            ),
            ("remove_field", ["tests", "post", "text"]),
            ("rename_field", ["tests", "comment", "user", "author"]),
            (
                "alter_field",
                [
                    "tests",
                    "comment",
                    "user",
                    models.IntegerField(),
                    True,
                ],
            ),
        ]
        for method, args in tests:
            with self.subTest(method=method):
                project_state = self.get_base_project_state()
                getattr(project_state, method)(*args)
                
                self.assertIsNone(project_state._relations)
                self.assertEqual(project_state.relations, project_state._relations)
                self.assertIsNotNone(project_state._relations)

    def test_add_model(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        self.assertEqual(
            list(project_state.relations["tests", "comment"]),
            [("tests", "comment")],
        )
        self.assertNotIn(("tests", "post"), project_state.relations)

    def test_add_model_no_relations(self):
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="Tag",
                fields=[("id", models.AutoField(primary_key=True))],
            )
        )
        self.assertEqual(project_state.relations, {})

    def test_add_model_other_app(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        project_state.add_model(
            ModelState(
                app_label="tests_other",
                name="comment",
                fields=[
                    ("id", models.AutoField(primary_key=True)),
                    ("user", models.ForeignKey("tests.user", models.CASCADE)),
                ],
            )
        )
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post"), ("tests_other", "comment")],
        )

    def test_remove_model(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        self.assertEqual(
            list(project_state.relations["tests", "comment"]),
            [("tests", "comment")],
        )

        project_state.remove_model("tests", "comment")
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "post")],
        )
        self.assertNotIn(("tests", "comment"), project_state.relations)
        project_state.remove_model("tests", "post")
        self.assertEqual(project_state.relations, {})
        project_state.remove_model("tests", "user")
        self.assertEqual(project_state.relations, {})

    def test_rename_model(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        self.assertEqual(
            list(project_state.relations["tests", "comment"]),
            [("tests", "comment")],
        )

        related_field = project_state.relations["tests", "user"]["tests", "comment"]
        project_state.rename_model("tests", "comment", "opinion")
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "post"), ("tests", "opinion")],
        )
        self.assertEqual(
            list(project_state.relations["tests", "opinion"]),
            [("tests", "opinion")],
        )
        self.assertNotIn(("tests", "comment"), project_state.relations)
        self.assertEqual(
            project_state.relations["tests", "user"]["tests", "opinion"],
            related_field,
        )

        project_state.rename_model("tests", "user", "author")
        self.assertEqual(
            list(project_state.relations["tests", "author"]),
            [("tests", "post"), ("tests", "opinion")],
        )
        self.assertNotIn(("tests", "user"), project_state.relations)

    def test_rename_model_no_relations(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        related_field = project_state.relations["tests", "user"]["tests", "post"]
        self.assertNotIn(("tests", "post"), project_state.relations)
        
        project_state.rename_model("tests", "post", "blog")
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "blog")],
        )
        self.assertNotIn(("tests", "blog"), project_state.relations)
        self.assertEqual(
            related_field,
            project_state.relations["tests", "user"]["tests", "blog"],
        )

    def test_add_field(self):
        project_state = self.get_base_project_state()
        self.assertNotIn(("tests", "post"), project_state.relations)
        
        new_field = models.ForeignKey("self", models.CASCADE)
        project_state.add_field(
            "tests",
            "post",
            "next_post",
            new_field,
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "post"]),
            [("tests", "post")],
        )
        self.assertEqual(
            project_state.relations["tests", "post"]["tests", "post"],
            {"next_post": new_field},
        )
        
        new_field = models.ForeignKey("tests.post", models.CASCADE)
        project_state.add_field(
            "tests",
            "comment",
            "post",
            new_field,
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "post"]),
            [("tests", "post"), ("tests", "comment")],
        )
        self.assertEqual(
            project_state.relations["tests", "post"]["tests", "comment"],
            {"post": new_field},
        )

    def test_add_field_m2m_with_through(self):
        project_state = self.get_base_project_state()
        project_state.add_model(
            ModelState(
                app_label="tests",
                name="Tag",
                fields=[("id", models.AutoField(primary_key=True))],
            )
        )
        project_state.add_model(
            ModelState(
                app_label="tests",
                name="PostTag",
                fields=[
                    ("id", models.AutoField(primary_key=True)),
                    ("post", models.ForeignKey("tests.post", models.CASCADE)),
                    ("tag", models.ForeignKey("tests.tag", models.CASCADE)),
                ],
            )
        )
        self.assertEqual(
            list(project_state.relations["tests", "post"]),
            [("tests", "posttag")],
        )
        self.assertEqual(
            list(project_state.relations["tests", "tag"]),
            [("tests", "posttag")],
        )
        
        new_field = models.ManyToManyField("tests.tag", through="tests.posttag")
        project_state.add_field(
            "tests",
            "post",
            "tags",
            new_field,
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "post"]),
            [("tests", "posttag")],
        )
        self.assertEqual(
            list(project_state.relations["tests", "tag"]),
            [("tests", "posttag"), ("tests", "post")],
        )
        self.assertEqual(
            project_state.relations["tests", "tag"]["tests", "post"],
            {"tags": new_field},
        )

    def test_remove_field(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        
        project_state.remove_field("tests", "post", "authors")
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment")],
        )
        
        project_state.remove_field("tests", "comment", "user")
        self.assertEqual(project_state.relations["tests", "user"], {})

    def test_remove_field_no_relations(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        
        project_state.remove_field("tests", "post", "text")
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )

    def test_rename_field(self):
        project_state = self.get_base_project_state()
        field = project_state.models["tests", "comment"].fields["user"]
        self.assertEqual(
            project_state.relations["tests", "user"]["tests", "comment"],
            {"user": field},
        )

        project_state.rename_field("tests", "comment", "user", "author")
        renamed_field = project_state.models["tests", "comment"].fields["author"]
        self.assertEqual(
            project_state.relations["tests", "user"]["tests", "comment"],
            {"author": renamed_field},
        )
        self.assertEqual(field, renamed_field)

    def test_rename_field_no_relations(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        
        project_state.rename_field("tests", "post", "text", "description")
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )

    def test_alter_field(self):
        project_state = self.get_base_project_state()
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        
        project_state.alter_field(
            "tests",
            "comment",
            "user",
            models.IntegerField(),
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "post")],
        )
        
        m2m_field = models.ManyToManyField("tests.user")
        project_state.alter_field(
            "tests",
            "comment",
            "user",
            m2m_field,
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "post"), ("tests", "comment")],
        )
        self.assertEqual(
            project_state.relations["tests", "user"]["tests", "comment"],
            {"user": m2m_field},
        )

    def test_alter_field_m2m_to_fk(self):
        project_state = self.get_base_project_state()
        project_state.add_model(
            ModelState(
                app_label="tests_other",
                name="user_other",
                fields=[("id", models.AutoField(primary_key=True))],
            )
        )
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        self.assertNotIn(("tests_other", "user_other"), project_state.relations)
        
        foreign_key = models.ForeignKey("tests_other.user_other", models.CASCADE)
        project_state.alter_field(
            "tests",
            "post",
            "authors",
            foreign_key,
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment")],
        )
        self.assertEqual(
            list(project_state.relations["tests_other", "user_other"]),
            [("tests", "post")],
        )
        self.assertEqual(
            project_state.relations["tests_other", "user_other"]["tests", "post"],
            {"authors": foreign_key},
        )

    def test_many_relations_to_same_model(self):
        project_state = self.get_base_project_state()
        new_field = models.ForeignKey("tests.user", models.CASCADE)
        project_state.add_field(
            "tests",
            "comment",
            "reviewer",
            new_field,
            preserve_default=True,
        )
        self.assertEqual(
            list(project_state.relations["tests", "user"]),
            [("tests", "comment"), ("tests", "post")],
        )
        comment_rels = project_state.relations["tests", "user"]["tests", "comment"]
        
        self.assertEqual(len(comment_rels), 2)
        self.assertEqual(comment_rels["reviewer"], new_field)
        
        project_state.rename_field("tests", "comment", "reviewer", "supervisor")
        self.assertEqual(len(comment_rels), 2)
        self.assertEqual(comment_rels["supervisor"], new_field)
        
        project_state.remove_field("tests", "comment", "user")
        self.assertEqual(comment_rels, {"supervisor": new_field})


class ModelStateTests(SimpleTestCase):
    def test_custom_model_base(self):
        state = ModelState.from_model(ModelWithCustomBase)
        self.assertEqual(state.bases, (models.Model,))

    def test_bound_field_sanity_check(self):
        field = models.CharField(max_length=1)
        field.model = models.Model
        with self.assertRaisesMessage(
            ValueError, 'ModelState.fields cannot be bound to a model - "field" is.'
        ):
            ModelState("app", "Model", [("field", field)])

    def test_sanity_check_to(self):
        field = models.ForeignKey(UnicodeModel, models.CASCADE)
        with self.assertRaisesMessage(
            ValueError,
            'Model fields in "ModelState.fields" cannot refer to a model class - '
            '"app.Model.field.to" does. Use a string reference instead.',
        ):
            ModelState("app", "Model", [("field", field)])

    def test_sanity_check_through(self):
        field = models.ManyToManyField("UnicodeModel")
        field.remote_field.through = UnicodeModel
        with self.assertRaisesMessage(
            ValueError,
            'Model fields in "ModelState.fields" cannot refer to a model class - '
            '"app.Model.field.through" does. Use a string reference instead.',
        ):
            ModelState("app", "Model", [("field", field)])

    def test_sanity_index_name(self):
        field = models.IntegerField()
        options = {"indexes": [models.Index(fields=["field"])]}
        msg = (
            "Indexes passed to ModelState require a name attribute. <Index: "
            "fields=['field']> doesn't have one."
        )
        with self.assertRaisesMessage(ValueError, msg):
            ModelState("app", "Model", [("field", field)], options=options)

    def test_fields_immutability(self):
        
        apps = Apps()
        field = models.CharField(max_length=1)
        state = ModelState("app", "Model", [("name", field)])
        Model = state.render(apps)
        self.assertNotEqual(Model._meta.get_field("name"), field)

    def test_repr(self):
        field = models.CharField(max_length=1)
        state = ModelState(
            "app", "Model", [("name", field)], bases=["app.A", "app.B", "app.C"]
        )
        self.assertEqual(repr(state), "<ModelState: 'app.Model'>")

        project_state = ProjectState()
        project_state.add_model(state)
        with self.assertRaisesMessage(
            InvalidBasesError, "Cannot resolve bases for [<ModelState: 'app.Model'>]"
        ):
            project_state.apps

    def test_fields_ordering_equality(self):
        state = ModelState(
            "migrations",
            "Tag",
            [
                ("id", models.AutoField(primary_key=True)),
                ("name", models.CharField(max_length=100)),
                ("hidden", models.BooleanField()),
            ],
        )
        reordered_state = ModelState(
            "migrations",
            "Tag",
            [
                ("id", models.AutoField(primary_key=True)),
                
                ("hidden", models.BooleanField()),
                ("name", models.CharField(max_length=100)),
            ],
        )
        self.assertEqual(state, reordered_state)

    @override_settings(TEST_SWAPPABLE_MODEL="migrations.SomeFakeModel")
    def test_create_swappable(self):
        
        new_apps = Apps(["migrations"])

        class Author(models.Model):
            name = models.CharField(max_length=255)
            bio = models.TextField()
            age = models.IntegerField(blank=True, null=True)

            class Meta:
                app_label = "migrations"
                apps = new_apps
                swappable = "TEST_SWAPPABLE_MODEL"

        author_state = ModelState.from_model(Author)
        self.assertEqual(author_state.app_label, "migrations")
        self.assertEqual(author_state.name, "Author")
        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, [])

    @override_settings(TEST_SWAPPABLE_MODEL="migrations.SomeFakeModel")
    def test_create_swappable_from_abstract(self):
        
        new_apps = Apps(["migrations"])

        class SearchableLocation(models.Model):
            keywords = models.CharField(max_length=256)

            class Meta:
                app_label = "migrations"
                apps = new_apps

        class Station(SearchableLocation):
            name = models.CharField(max_length=128)

            class Meta:
                abstract = True

        class BusStation(Station):
            bus_routes = models.CharField(max_length=128)
            inbound = models.BooleanField(default=False)

            class Meta(Station.Meta):
                app_label = "migrations"
                apps = new_apps
                swappable = "TEST_SWAPPABLE_MODEL"

        station_state = ModelState.from_model(BusStation)
        self.assertEqual(station_state.app_label, "migrations")
        self.assertEqual(station_state.name, "BusStation")
        self.assertEqual(
            list(station_state.fields),
            ["searchablelocation_ptr", "name", "bus_routes", "inbound"],
        )
        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": [],
            },
        )
        self.assertEqual(station_state.bases, ("migrations.searchablelocation",))
        self.assertEqual(station_state.managers, [])

    @override_settings(TEST_SWAPPABLE_MODEL="migrations.SomeFakeModel")
    def test_custom_manager_swappable(self):
        
        new_apps = Apps(["migrations"])

        class Food(models.Model):
            food_mgr = FoodManager("a", "b")
            food_qs = FoodQuerySet.as_manager()
            food_no_mgr = NoMigrationFoodManager("x", "y")

            class Meta:
                app_label = "migrations"
                apps = new_apps
                swappable = "TEST_SWAPPABLE_MODEL"

        food_state = ModelState.from_model(Food)

        
        self.assertEqual([name for name, mgr in food_state.managers], ["food_mgr"])
        self.assertEqual(food_state.managers[0][1].args, ("a", "b", 1, 2))

    @isolate_apps("migrations", "django.contrib.contenttypes")
    def test_order_with_respect_to_private_field(self):
        class PrivateFieldModel(models.Model):
            content_type = models.ForeignKey("contenttypes.ContentType", models.CASCADE)
            object_id = models.PositiveIntegerField()
            private = GenericForeignKey()

            class Meta:
                order_with_respect_to = "private"

        state = ModelState.from_model(PrivateFieldModel)
        self.assertNotIn("order_with_respect_to", state.options)

    @isolate_apps("migrations")
    def test_abstract_model_children_inherit_indexes(self):
        class Abstract(models.Model):
            name = models.CharField(max_length=50)

            class Meta:
                app_label = "migrations"
                abstract = True
                indexes = [models.Index(fields=["name"])]

        class Child1(Abstract):
            pass

        class Child2(Abstract):
            pass

        child1_state = ModelState.from_model(Child1)
        child2_state = ModelState.from_model(Child2)
        index_names = [index.name for index in child1_state.options["indexes"]]
        self.assertEqual(index_names, ["migrations__name_b0afd7_idx"])
        index_names = [index.name for index in child2_state.options["indexes"]]
        self.assertEqual(index_names, ["migrations__name_016466_idx"])

        
        child1_state.options["indexes"][0].name = "bar"
        self.assertEqual(Child1._meta.indexes[0].name, "migrations__name_b0afd7_idx")

    @isolate_apps("migrations")
    def test_explicit_index_name(self):
        class TestModel(models.Model):
            name = models.CharField(max_length=50)

            class Meta:
                app_label = "migrations"
                indexes = [models.Index(fields=["name"], name="foo_idx")]

        model_state = ModelState.from_model(TestModel)
        index_names = [index.name for index in model_state.options["indexes"]]
        self.assertEqual(index_names, ["foo_idx"])

    @isolate_apps("migrations")
    def test_from_model_constraints(self):
        class ModelWithConstraints(models.Model):
            size = models.IntegerField()

            class Meta:
                constraints = [
                    models.CheckConstraint(check=models.Q(size__gt=1), name="size_gt_1")
                ]

        state = ModelState.from_model(ModelWithConstraints)
        model_constraints = ModelWithConstraints._meta.constraints
        state_constraints = state.options["constraints"]
        self.assertEqual(model_constraints, state_constraints)
        self.assertIsNot(model_constraints, state_constraints)
        self.assertIsNot(model_constraints[0], state_constraints[0])


class RelatedModelsTests(SimpleTestCase):
    def setUp(self):
        self.apps = Apps(["migrations.related_models_app"])

    def create_model(
        self, name, foreign_keys=[], bases=(), abstract=False, proxy=False
    ):
        test_name = "related_models_app"
        assert not (abstract and proxy)
        meta_contents = {
            "abstract": abstract,
            "app_label": test_name,
            "apps": self.apps,
            "proxy": proxy,
        }
        meta = type("Meta", (), meta_contents)
        if not bases:
            bases = (models.Model,)
        body = {
            "Meta": meta,
            "__module__": "__fake__",
        }
        fname_base = fname = "%s_%%d" % name.lower()
        for i, fk in enumerate(foreign_keys, 1):
            fname = fname_base % i
            body[fname] = fk
        return type(name, bases, body)

    def assertRelated(self, model, needle):
        self.assertEqual(
            get_related_models_recursive(model),
            {(n._meta.app_label, n._meta.model_name) for n in needle},
        )

    def test_unrelated(self):
        A = self.create_model("A")
        B = self.create_model("B")
        self.assertRelated(A, [])
        self.assertRelated(B, [])

    def test_direct_fk(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("B", models.CASCADE)]
        )
        B = self.create_model("B")
        self.assertRelated(A, [B])
        self.assertRelated(B, [A])

    def test_direct_hidden_fk(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("B", models.CASCADE, related_name="+")]
        )
        B = self.create_model("B")
        self.assertRelated(A, [B])
        self.assertRelated(B, [A])

    def test_fk_through_proxy(self):
        A = self.create_model("A")
        B = self.create_model("B", bases=(A,), proxy=True)
        C = self.create_model("C", bases=(B,), proxy=True)
        D = self.create_model(
            "D", foreign_keys=[models.ForeignKey("C", models.CASCADE)]
        )
        self.assertRelated(A, [B, C, D])
        self.assertRelated(B, [A, C, D])
        self.assertRelated(C, [A, B, D])
        self.assertRelated(D, [A, B, C])

    def test_nested_fk(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("B", models.CASCADE)]
        )
        B = self.create_model(
            "B", foreign_keys=[models.ForeignKey("C", models.CASCADE)]
        )
        C = self.create_model("C")
        self.assertRelated(A, [B, C])
        self.assertRelated(B, [A, C])
        self.assertRelated(C, [A, B])

    def test_two_sided(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("B", models.CASCADE)]
        )
        B = self.create_model(
            "B", foreign_keys=[models.ForeignKey("A", models.CASCADE)]
        )
        self.assertRelated(A, [B])
        self.assertRelated(B, [A])

    def test_circle(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("B", models.CASCADE)]
        )
        B = self.create_model(
            "B", foreign_keys=[models.ForeignKey("C", models.CASCADE)]
        )
        C = self.create_model(
            "C", foreign_keys=[models.ForeignKey("A", models.CASCADE)]
        )
        self.assertRelated(A, [B, C])
        self.assertRelated(B, [A, C])
        self.assertRelated(C, [A, B])

    def test_base(self):
        A = self.create_model("A")
        B = self.create_model("B", bases=(A,))
        self.assertRelated(A, [B])
        self.assertRelated(B, [A])

    def test_nested_base(self):
        A = self.create_model("A")
        B = self.create_model("B", bases=(A,))
        C = self.create_model("C", bases=(B,))
        self.assertRelated(A, [B, C])
        self.assertRelated(B, [A, C])
        self.assertRelated(C, [A, B])

    def test_multiple_bases(self):
        A = self.create_model("A")
        B = self.create_model("B")
        C = self.create_model(
            "C",
            bases=(
                A,
                B,
            ),
        )
        self.assertRelated(A, [B, C])
        self.assertRelated(B, [A, C])
        self.assertRelated(C, [A, B])

    def test_multiple_nested_bases(self):
        A = self.create_model("A")
        B = self.create_model("B")
        C = self.create_model(
            "C",
            bases=(
                A,
                B,
            ),
        )
        D = self.create_model("D")
        E = self.create_model("E", bases=(D,))
        F = self.create_model(
            "F",
            bases=(
                C,
                E,
            ),
        )
        Y = self.create_model("Y")
        Z = self.create_model("Z", bases=(Y,))
        self.assertRelated(A, [B, C, D, E, F])
        self.assertRelated(B, [A, C, D, E, F])
        self.assertRelated(C, [A, B, D, E, F])
        self.assertRelated(D, [A, B, C, E, F])
        self.assertRelated(E, [A, B, C, D, F])
        self.assertRelated(F, [A, B, C, D, E])
        self.assertRelated(Y, [Z])
        self.assertRelated(Z, [Y])

    def test_base_to_base_fk(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("Y", models.CASCADE)]
        )
        B = self.create_model("B", bases=(A,))
        Y = self.create_model("Y")
        Z = self.create_model("Z", bases=(Y,))
        self.assertRelated(A, [B, Y, Z])
        self.assertRelated(B, [A, Y, Z])
        self.assertRelated(Y, [A, B, Z])
        self.assertRelated(Z, [A, B, Y])

    def test_base_to_subclass_fk(self):
        A = self.create_model(
            "A", foreign_keys=[models.ForeignKey("Z", models.CASCADE)]
        )
        B = self.create_model("B", bases=(A,))
        Y = self.create_model("Y")
        Z = self.create_model("Z", bases=(Y,))
        self.assertRelated(A, [B, Y, Z])
        self.assertRelated(B, [A, Y, Z])
        self.assertRelated(Y, [A, B, Z])
        self.assertRelated(Z, [A, B, Y])

    def test_direct_m2m(self):
        A = self.create_model("A", foreign_keys=[models.ManyToManyField("B")])
        B = self.create_model("B")
        self.assertRelated(A, [A.a_1.rel.through, B])
        self.assertRelated(B, [A, A.a_1.rel.through])

    def test_direct_m2m_self(self):
        A = self.create_model("A", foreign_keys=[models.ManyToManyField("A")])
        self.assertRelated(A, [A.a_1.rel.through])

    def test_intermediate_m2m_self(self):
        A = self.create_model(
            "A", foreign_keys=[models.ManyToManyField("A", through="T")]
        )
        T = self.create_model(
            "T",
            foreign_keys=[
                models.ForeignKey("A", models.CASCADE),
                models.ForeignKey("A", models.CASCADE),
            ],
        )
        self.assertRelated(A, [T])
        self.assertRelated(T, [A])

    def test_intermediate_m2m(self):
        A = self.create_model(
            "A", foreign_keys=[models.ManyToManyField("B", through="T")]
        )
        B = self.create_model("B")
        T = self.create_model(
            "T",
            foreign_keys=[
                models.ForeignKey("A", models.CASCADE),
                models.ForeignKey("B", models.CASCADE),
            ],
        )
        self.assertRelated(A, [B, T])
        self.assertRelated(B, [A, T])
        self.assertRelated(T, [A, B])

    def test_intermediate_m2m_extern_fk(self):
        A = self.create_model(
            "A", foreign_keys=[models.ManyToManyField("B", through="T")]
        )
        B = self.create_model("B")
        Z = self.create_model("Z")
        T = self.create_model(
            "T",
            foreign_keys=[
                models.ForeignKey("A", models.CASCADE),
                models.ForeignKey("B", models.CASCADE),
                models.ForeignKey("Z", models.CASCADE),
            ],
        )
        self.assertRelated(A, [B, T, Z])
        self.assertRelated(B, [A, T, Z])
        self.assertRelated(T, [A, B, Z])
        self.assertRelated(Z, [A, B, T])

    def test_intermediate_m2m_base(self):
        A = self.create_model(
            "A", foreign_keys=[models.ManyToManyField("B", through="T")]
        )
        B = self.create_model("B")
        S = self.create_model("S")
        T = self.create_model(
            "T",
            foreign_keys=[
                models.ForeignKey("A", models.CASCADE),
                models.ForeignKey("B", models.CASCADE),
            ],
            bases=(S,),
        )
        self.assertRelated(A, [B, S, T])
        self.assertRelated(B, [A, S, T])
        self.assertRelated(S, [A, B, T])
        self.assertRelated(T, [A, B, S])

    def test_generic_fk(self):
        A = self.create_model(
            "A",
            foreign_keys=[
                models.ForeignKey("B", models.CASCADE),
                GenericForeignKey(),
            ],
        )
        B = self.create_model(
            "B",
            foreign_keys=[
                models.ForeignKey("C", models.CASCADE),
            ],
        )
        self.assertRelated(A, [B])
        self.assertRelated(B, [A])

    def test_abstract_base(self):
        A = self.create_model("A", abstract=True)
        B = self.create_model("B", bases=(A,))
        self.assertRelated(A, [B])
        self.assertRelated(B, [])

    def test_nested_abstract_base(self):
        A = self.create_model("A", abstract=True)
        B = self.create_model("B", bases=(A,), abstract=True)
        C = self.create_model("C", bases=(B,))
        self.assertRelated(A, [B, C])
        self.assertRelated(B, [C])
        self.assertRelated(C, [])

    def test_proxy_base(self):
        A = self.create_model("A")
        B = self.create_model("B", bases=(A,), proxy=True)
        self.assertRelated(A, [B])
        self.assertRelated(B, [])

    def test_nested_proxy_base(self):
        A = self.create_model("A")
        B = self.create_model("B", bases=(A,), proxy=True)
        C = self.create_model("C", bases=(B,), proxy=True)
        self.assertRelated(A, [B, C])
        self.assertRelated(B, [C])
        self.assertRelated(C, [])

    def test_multiple_mixed_bases(self):
        A = self.create_model("A", abstract=True)
        M = self.create_model("M")
        P = self.create_model("P")
        Q = self.create_model("Q", bases=(P,), proxy=True)
        Z = self.create_model("Z", bases=(A, M, Q))
        
        self.assertRelated(A, [M, P, Q, Z])
        self.assertRelated(M, [P, Q, Z])
        self.assertRelated(P, [M, Q, Z])
        self.assertRelated(Q, [M, P, Z])
        self.assertRelated(Z, [M, P, Q])