Example #1
0
    def test_ignore_order_wrt(self):
        """
        Makes sure ProjectState doesn't include OrderWrt fields when
        making from existing models.
        """
        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"

        # Make a valid ProjectState and render it
        project_state = ProjectState()
        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],
            ["id", "author"],
        )
Example #2
0
    def test_real_apps(self):
        """
        Tests that including real apps can resolve dangling FK errors.
        This test relies on the fact that contenttypes is always loaded.
        """
        new_apps = Apps()

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

        # If we just stick it into an empty state it should fail
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(TestModel))
        with self.assertRaises(ValueError):
            project_state.apps

        # If we include the real app it should succeed
        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,
        )
Example #3
0
    def test_dangling_references_throw_error(self):
        class Author(models.Model):
            name = models.TextField()

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

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

        # Make a valid ProjectState and render it
        project_state = ProjectState()
        project_state.add_model_state(ModelState.from_model(Author))
        project_state.add_model_state(ModelState.from_model(Book))
        project_state.add_model_state(ModelState.from_model(Magazine))
        rendered_state = project_state.render()
        self.assertEqual(len(rendered_state.get_models()), 3)

        # now make an invalid one with a ForeignKey
        project_state = ProjectState()
        project_state.add_model_state(ModelState.from_model(Book))
        with self.assertRaises(ValueError):
            rendered_state = project_state.render()

        # and another with ManyToManyField
        project_state = ProjectState()
        project_state.add_model_state(ModelState.from_model(Magazine))
        with self.assertRaises(ValueError):
            rendered_state = project_state.render()
Example #4
0
    def test_add_relations(self):
        """
        #24573 - Adding relations to existing models should reload the
        referenced models too.
        """
        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  # We need to work with rendered models

        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')
        # Check that the relations between the old models are correct
        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')

        # Check that all models have changed
        self.assertIsNot(model_a_old, model_a_new)
        self.assertIsNot(model_b_old, model_b_new)
        self.assertIsNot(model_c_old, model_c_new)
        # Check that the relations between the old models still hold
        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)
        # Check that the relations between the new models correct
        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)
Example #5
0
    def test_render_project_dependencies(self):
        """
        Tests that the ProjectState render method correctly renders models
        to account for inter-model base dependencies.
        """
        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

        # Make a ProjectState and render it
        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)

        # Now make an invalid ProjectState and make sure it fails
        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
Example #6
0
 def test_fields_immutability(self):
     """
     Tests that rendering a model state doesn't alter its internal fields.
     """
     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)
Example #7
0
    def test_remove_relations(self):
        """
        #24225 - Tests that relations between models are updated while
        remaining the relations and references for models of an old state.
        """
        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)
        # Tests that model from old_state still has the relation
        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)

        # Same test for deleted model
        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)
Example #8
0
    def test_create_swappable(self):
        """
        Tests making a ProjectState from an Apps with a swappable model
        """
        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([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(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL'})
        self.assertEqual(author_state.bases, (models.Model, ))
        self.assertEqual(author_state.managers, [])
Example #9
0
 def state_forwards(self, app_label, state):
     state.models[app_label, self.name.lower()] = ModelState(
         app_label,
         self.name,
         list(self.fields),
         dict(self.options),
         tuple(self.bases),
     )
Example #10
0
 def test_sanity_check_through(self):
     field = models.ManyToManyField('UnicodeModel')
     field.remote_field.through = UnicodeModel
     with self.assertRaisesMessage(
             ValueError,
             'ModelState.fields cannot refer to a model class - "field.through" does. '
             'Use a string reference instead.'):
         ModelState('app', 'Model', [('field', field)])
Example #11
0
 def test_sanity_check_to(self):
     field = models.ForeignKey(UnicodeModel, models.CASCADE)
     with self.assertRaisesMessage(
         ValueError,
         'ModelState.fields cannot refer to a model class - "field.to" does. '
         'Use a string reference instead.'
     ):
         ModelState('app', 'Model', [('field', field)])
Example #12
0
    def test_remove_relations(self):
        """
        #24225 - Tests that relations between models are updated while
        remaining the relations and references for models of an old state.
        """
        class A(models.Model):
            class Meta:
                app_label = "something"

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

            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))
        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)
        # Tests that model from old_state still has the relation
        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)

        # Same test for deleted model
        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)
Example #13
0
    def test_index_together_migration(self):
        class IndexTogetherModel2(TranslatableModel):
            sfield_a = models.CharField(max_length=250)
            sfield_b = models.CharField(max_length=250)
            translations = TranslatedFields(
                tfield_a = models.CharField(max_length=250),
                tfield_b = models.CharField(max_length=250),
            )
            class Meta:
                index_together = [('sfield_a', 'sfield_b'), ('tfield_a', 'tfield_b')]

        from django.db.migrations.state import ModelState
        state = ModelState.from_model(IndexTogetherModel2)
        self.assertEqual(state.options['index_together'], {('sfield_a', 'sfield_b')})

        state = ModelState.from_model(IndexTogetherModel2._meta.translations_model)
        self.assertEqual(state.options['index_together'], {('tfield_a', 'tfield_b')})
Example #14
0
    def test_render_model_with_multiple_inheritance(self):
        class Foo(models.Model):
            class Meta:
                app_label = "migrations"
                app_cache = AppCache()

        class Bar(models.Model):
            class Meta:
                app_label = "migrations"
                app_cache = AppCache()

        class FooBar(Foo, Bar):
            class Meta:
                app_label = "migrations"
                app_cache = AppCache()

        app_cache = AppCache()

        # We shouldn't be able to render yet
        ms = ModelState.from_model(FooBar)
        with self.assertRaises(InvalidBasesError):
            ms.render(app_cache)

        # Once the parent models are in the app cache, it should be fine
        ModelState.from_model(Foo).render(app_cache)
        ModelState.from_model(Bar).render(app_cache)
        ModelState.from_model(FooBar).render(app_cache)
Example #15
0
    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()

        apps = Apps(["migrations"])

        # We shouldn't be able to render yet
        ms = ModelState.from_model(FooBar)
        with self.assertRaises(InvalidBasesError):
            ms.render(apps)

        # Once the parent models are in the app registry, it should be fine
        ModelState.from_model(Foo).render(apps)
        ModelState.from_model(Bar).render(apps)
        ModelState.from_model(FooBar).render(apps)
Example #16
0
    def test_render_multiple_inheritance(self):
        # Use a custom app cache to avoid polluting the global one.
        new_app_cache = BaseAppCache()

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

            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache

        class Novel(Book):
            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache

        yet_another_app_cache = BaseAppCache()
        ModelState.from_model(Novel).render(yet_another_app_cache)
Example #17
0
    def test_render_multiple_inheritance(self):
        # Use a custom app cache to avoid polluting the global one.
        new_app_cache = BaseAppCache()

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

            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache

        class Novel(Book):
            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache

        yet_another_app_cache = BaseAppCache()
        ModelState.from_model(Novel).render(yet_another_app_cache)
Example #18
0
    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
Example #19
0
 def state_forwards(self, app_label, state):
     state.add_model(ModelState(
         app_label,
         self.name,
         list(self.fields),
         dict(self.options),
         tuple(self.bases),
         list(self.managers),
     ))
Example #20
0
    def test_unique_together_migration(self):
        class UniqueTogetherModel3(TranslatableModel):
            sfield_a = models.CharField(max_length=250)
            sfield_b = models.CharField(max_length=250)
            translations = TranslatedFields(
                tfield_a = models.CharField(max_length=250),
                tfield_b = models.CharField(max_length=250),
            )
            class Meta:
                unique_together = [('sfield_a', 'sfield_b'), ('tfield_a', 'tfield_b')]

        from django.db.migrations.state import ModelState
        state = ModelState.from_model(UniqueTogetherModel3)
        self.assertEqual(state.options['unique_together'], {('sfield_a', 'sfield_b')})

        state = ModelState.from_model(UniqueTogetherModel3._meta.translations_model)
        self.assertEqual(state.options['unique_together'], {('language_code', 'master'),
                                                            ('tfield_a', 'tfield_b')})
Example #21
0
def create_gm2m_intermediary_model(field, klass):
    """
    Creates a generic M2M model for the GM2M field 'field' on model 'klass'
    """

    from django.db import models

    managed = klass._meta.managed
    name = "%s_%s" % (klass._meta.object_name, field.name)

    model_name = klass._meta.model_name

    db_table = db_backends_utils.truncate_name(
        "%s_%s" % (klass._meta.db_table, field.name), connection.ops.max_name_length()
    )

    meta_kwargs = {
        "db_table": db_table,
        "managed": managed,
        "auto_created": klass,
        "app_label": klass._meta.app_label,
        "db_tablespace": klass._meta.db_tablespace,
        "unique_together": (SRC_ATTNAME, CT_ATTNAME, FK_ATTNAME),
        "verbose_name": "%s-generic relationship" % model_name,
        "verbose_name_plural": "%s-generic relationships" % model_name,
        "apps": field.model._meta.apps,
    }

    meta = type("Meta", (object,), meta_kwargs)

    fk_maxlength = 16  # default value
    if field.pk_maxlength is not False:
        fk_maxlength = field.pk_maxlength

    model = type(
        str(name),
        (models.Model,),
        {
            "Meta": meta,
            "__module__": klass.__module__,
            SRC_ATTNAME: models.ForeignKey(
                klass, on_delete=field.rel.on_delete_src, db_constraint=field.rel.db_constraint
            ),
            CT_ATTNAME: models.ForeignKey(ct.ContentType, db_constraint=field.rel.db_constraint),
            FK_ATTNAME: models.CharField(max_length=fk_maxlength),
            TGT_ATTNAME: ct.GenericForeignKey(
                ct_field=CT_ATTNAME, fk_field=FK_ATTNAME, for_concrete_model=field.rel.for_concrete_model
            ),
        },
    )

    if is_fake_model(klass):
        # if we are building a fake model for migrations purposes, create a
        # ModelState from the model and render it (see issues #3 and #5)
        return ModelState.from_model(model).render(klass._meta.apps)

    return model
Example #22
0
def create_gm2m_intermediary_model(field, klass):
    """
    Creates a generic M2M model for the GM2M field 'field' on model 'klass'
    """

    from django.db import models

    managed = klass._meta.managed
    name = '%s_%s' % (klass._meta.object_name, field.name)

    model_name = klass._meta.model_name

    db_table = db_backends_utils.truncate_name(
                   '%s_%s' % (klass._meta.db_table, field.name),
                   connection.ops.max_name_length())

    meta_kwargs = {
        'db_table': db_table,
        'managed': managed,
        'auto_created': klass,
        'app_label': klass._meta.app_label,
        'db_tablespace': klass._meta.db_tablespace,
        'unique_together': (SRC_ATTNAME, CT_ATTNAME, FK_ATTNAME),
        'verbose_name': '%s-generic relationship' % model_name,
        'verbose_name_plural': '%s-generic relationships' % model_name,
        'apps': field.model._meta.apps,
    }

    meta = type('Meta', (object,), meta_kwargs)

    fk_maxlength = 16  # default value
    if field.pk_maxlength is not False:
        fk_maxlength = field.pk_maxlength

    model = type(str(name), (models.Model,), {
        'Meta': meta,
        '__module__': klass.__module__,
        SRC_ATTNAME: models.ForeignKey(klass, auto_created=True,
                                       on_delete=field.remote_field.on_delete_src,
                                       db_constraint=field.remote_field.db_constraint),
        CT_ATTNAME: models.ForeignKey(ct.ContentType,
                                      db_constraint=field.remote_field.db_constraint),
        FK_ATTNAME: models.CharField(max_length=fk_maxlength),
        TGT_ATTNAME: ct.GenericForeignKey(
            ct_field=CT_ATTNAME,
            fk_field=FK_ATTNAME,
            for_concrete_model=field.remote_field.for_concrete_model,
        ),
    })

    if is_fake_model(klass):
        # if we are building a fake model for migrations purposes, create a
        # ModelState from the model and render it (see issues #3 and #5)
        return ModelState.from_model(model).render(klass._meta.apps)

    return model
Example #23
0
    def test_equality(self):
        """
        Tests that == and != are implemented correctly.
        """

        # Test two things that should be equal
        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  # Fill the apps cached property
        other_state = project_state.clone()
        self.assertEqual(project_state, project_state)
        self.assertEqual(project_state, other_state)
        self.assertEqual(project_state != project_state, False)
        self.assertEqual(project_state != other_state, False)
        self.assertNotEqual(project_state.apps, other_state.apps)

        # Make a very small change (max_len 99) and see if that affects it
        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.assertEqual(project_state == other_state, False)
Example #24
0
 def get_state(self):
     fields = [(field_def.name, field_def.construct())
               for field_def in self.fielddefinitions.select_subclasses()]
     options = self.get_model_opts()
     bases = self.get_model_bases()
     return ModelState(self.app_label,
                       self.object_name,
                       fields=fields,
                       options=options,
                       bases=bases)
Example #25
0
    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 Book(models.Model):
            author = models.ForeignKey(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

        # Make a valid ProjectState and render it
        project_state = ProjectState()
        project_state.add_model_state(ModelState.from_model(Author))
        project_state.add_model_state(ModelState.from_model(Book))
        project_state.add_model_state(ModelState.from_model(Magazine))
        rendered_state = project_state.render()
        self.assertEqual(len(rendered_state.get_models()), 3)

        # now make an invalid one with a ForeignKey
        project_state = ProjectState()
        project_state.add_model_state(ModelState.from_model(Book))
        with self.assertRaises(ValueError):
            rendered_state = project_state.render()

        # and another with ManyToManyField
        project_state = ProjectState()
        project_state.add_model_state(ModelState.from_model(Magazine))
        with self.assertRaises(ValueError):
            rendered_state = project_state.render()
Example #26
0
    def test_render_unique_app_labels(self):
        """
        Tests that the ProjectState render method doesn't raise an
        ImproperlyConfigured exception about unique labels if two dotted app
        names have the same last part.
        """
        class A(models.Model):
            class Meta:
                app_label = "django.contrib.auth"

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

        # Make a ProjectState and render it
        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)
Example #27
0
    def test_render_unique_app_labels(self):
        """
        Tests that the ProjectState render method doesn't raise an
        ImproperlyConfigured exception about unique labels if two dotted app
        names have the same last part.
        """
        class A(models.Model):
            class Meta:
                app_label = "django.contrib.auth"

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

        # Make a ProjectState and render it
        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)
Example #28
0
    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 Book(models.Model):
            author = models.ForeignKey(Author)

            class Meta:
                app_label = "migrations"
                apps = new_apps

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

        # Make a valid ProjectState and render it
        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)

        # now make an invalid one with a ForeignKey
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Book))
        with self.assertRaises(ValueError):
            project_state.apps

        # and another with ManyToManyField
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Magazine))
        with self.assertRaises(ValueError):
            project_state.apps
Example #29
0
 def get_related_model_states(cls, model_state):
     model_states = {}
     for _name, field in model_state.fields:
         related_model_reference = get_remote_field_model(field)
         if related_model_reference:
             related_model = cls._meta.apps.get_model(related_model_reference)
             if issubclass(related_model, MutableModel):
                 related_model_state = related_model.get_model_state(exclude_rels=True)
             else:
                 related_model_state = ModelState.from_model(related_model, exclude_rels=True)
             model_states[related_model_state.app_label, related_model_state.name] = related_model_state
             for base in related_model_state.bases:
                 if isinstance(base, string_types):
                     base_model = cls._meta.apps.get_model(base)
                     if issubclass(base_model, MutableModel):
                         base_model_state = base_model.get_model_state(exclude_rels=True)
                     else:
                         base_model_state = ModelState.from_model(base_model, exclude_rels=True)
                     model_states[base_model_state.app_label, base_model_state.name] = base_model_state
     return list(model_states.values())
Example #30
0
    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)
Example #31
0
 def create_model(entity):
     model = dynamic_models[entity.name]
     model_state = ModelState.from_model(model)
     ops.append(CreateModel(
         name=model_state.name,
         fields=model_state.fields,
         options=model_state.options,
         bases=model_state.bases,
         managers=model_state.managers
     ))
     created_models.add(entity.name)
Example #32
0
    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)
Example #33
0
    def test_render_multiple_inheritance(self):
        # Use a custom app cache to avoid polluting the global one.
        new_app_cache = BaseAppCache()

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

            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache

        class Novel(Book):
            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache

        # First, test rendering individually
        yet_another_app_cache = BaseAppCache()

        # We shouldn't be able to render yet
        with self.assertRaises(ValueError):
            ModelState.from_model(Novel).render(yet_another_app_cache)

        # Once the parent model is in the app cache, it should be fine
        ModelState.from_model(Book).render(yet_another_app_cache)
        ModelState.from_model(Novel).render(yet_another_app_cache)
Example #34
0
    def test_index_together_migration(self):
        class IndexTogetherModel2(TranslatableModel):
            sfield_a = models.CharField(max_length=250)
            sfield_b = models.CharField(max_length=250)
            translations = TranslatedFields(
                tfield_a=models.CharField(max_length=250),
                tfield_b=models.CharField(max_length=250),
            )

            class Meta:
                index_together = [('sfield_a', 'sfield_b'),
                                  ('tfield_a', 'tfield_b')]

        from django.db.migrations.state import ModelState
        state = ModelState.from_model(IndexTogetherModel2)
        self.assertEqual(state.options['index_together'],
                         {('sfield_a', 'sfield_b')})

        state = ModelState.from_model(
            IndexTogetherModel2._meta.translations_model)
        self.assertEqual(state.options['index_together'],
                         {('tfield_a', 'tfield_b')})
Example #35
0
    def test_runpython_manager_methods(self):
        def forwards(apps, schema_editor):
            UserModel = apps.get_model("auth", "User")
            user = UserModel.objects.create_user("user1", password="******")
            self.assertIsInstance(user, UserModel)

        operation = migrations.RunPython(forwards, migrations.RunPython.noop)
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(User))
        project_state.add_model(ModelState.from_model(Group))
        project_state.add_model(ModelState.from_model(Permission))
        project_state.add_model(ModelState.from_model(ContentType))
        new_state = project_state.clone()
        with connection.schema_editor() as editor:
            operation.state_forwards("test_manager_methods", new_state)
            operation.database_forwards(
                "test_manager_methods",
                editor,
                project_state,
                new_state,
            )
        user = User.objects.get(username="******")
        self.assertTrue(user.check_password("secure"))
Example #36
0
    def test_unique_together_migration(self):
        class UniqueTogetherModel3(TranslatableModel):
            sfield_a = models.CharField(max_length=250)
            sfield_b = models.CharField(max_length=250)
            translations = TranslatedFields(
                tfield_a=models.CharField(max_length=250),
                tfield_b=models.CharField(max_length=250),
            )

            class Meta:
                unique_together = [('sfield_a', 'sfield_b'),
                                   ('tfield_a', 'tfield_b')]

        from django.db.migrations.state import ModelState
        state = ModelState.from_model(UniqueTogetherModel3)
        self.assertEqual(state.options['unique_together'],
                         {('sfield_a', 'sfield_b')})

        state = ModelState.from_model(
            UniqueTogetherModel3._meta.translations_model)
        self.assertEqual(state.options['unique_together'],
                         {('language_code', 'master'),
                          ('tfield_a', 'tfield_b')})
Example #37
0
    def test_render(self):
        """
        Tests rendering a ProjectState into an Apps.
        """
        project_state = ProjectState()
        project_state.add_model_state(ModelState(
            "migrations",
            "Tag",
            [
                ("id", models.AutoField(primary_key=True)),
                ("name", models.CharField(max_length=100)),
                ("hidden", models.BooleanField()),
            ],
            {},
            None,
        ))
        project_state.add_model_state(ModelState(
            "migrations",
            "SubTag",
            [
                ('tag_ptr', models.OneToOneField(
                    auto_created=True,
                    primary_key=True,
                    to_field='id',
                    serialize=False,
                    to='migrations.Tag',
                )),
                ("awesome", models.BooleanField()),
            ],
            options={},
            bases=("migrations.Tag",),
        ))

        new_apps = project_state.render()
        self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("name")[0].max_length, 100)
        self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False)
        self.assertEqual(len(new_apps.get_model("migrations", "SubTag")._meta.local_fields), 2)
Example #38
0
    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()

        # First, test rendering individually
        apps = Apps(["migrations"])

        # We shouldn't be able to render yet
        ms = ModelState.from_model(Novel)
        with self.assertRaises(InvalidBasesError):
            ms.render(apps)

        # Once the parent model is in the app registry, it should be fine
        ModelState.from_model(Book).render(apps)
        ModelState.from_model(Novel).render(apps)
Example #39
0
    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()

        # First, test rendering individually
        apps = Apps(["migrations"])

        # We shouldn't be able to render yet
        ms = ModelState.from_model(Novel)
        with self.assertRaises(InvalidBasesError):
            ms.render(apps)

        # Once the parent model is in the app registry, it should be fine
        ModelState.from_model(Book).render(apps)
        ModelState.from_model(Novel).render(apps)
Example #40
0
    def test_self_relation(self):
        """
        #24513 - Modifying an object pointing to itself would cause it to be
        rendered twice and thus breaking its related M2M through objects.
        """
        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)
        )
        # 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.
        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)

        # Tests that the old model's _meta is still consistent
        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)

        # Tests that the new model's _meta is still consistent
        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)
Example #41
0
    def test_self_relation(self):
        """
        #24513 - Modifying an object pointing to itself would cause it to be
        rendered twice and thus breaking its related M2M through objects.
        """
        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)
        )
        # 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.
        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)

        # Tests that the old model's _meta is still consistent
        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)

        # Tests that the new model's _meta is still consistent
        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 gen_model_state(self, app_label, model_name, name=None, pk=1):
        options_dict = {}
        if name:
            options_dict = {
                "constraints": [
                    QuerysetConstraint(
                        name=name,
                        queryset=gen_m_object(app_label, model_name, name, pk),
                    )
                ]
            }

        return ModelState(
            app_label,
            model_name,
            [("id", models.AutoField(primary_key=True))],
            options_dict,
        )
Example #43
0
def get_create_sql_for_model(model):

    model_state = ModelState.from_model(model)

    # Create a fake migration with the CreateModel operation
    cm = operations.CreateModel(name=model_state.name, fields=model_state.fields)
    migration = Migration("fake_migration", "app")
    migration.operations.append(cm)

    # Let the migration framework think that the project is in an initial state
    state = ProjectState()

    # Get the SQL through the schema_editor bound to the connection
    connection = connections['default']
    with connection.schema_editor(collect_sql=True, atomic=migration.atomic) as schema_editor:
        state = migration.apply(state, schema_editor, collect_sql=True)

    # return the CREATE TABLE statement
    return "\n".join(schema_editor.collected_sql)
Example #44
0
    def test_render(self):
        """
        Tests rendering a ProjectState into an AppCache.
        """
        project_state = ProjectState()
        project_state.add_model_state(ModelState(
            "migrations",
            "Tag",
            [
                ("id", models.AutoField(primary_key=True)),
                ("name", models.CharField(max_length=100)),
                ("hidden", models.BooleanField()),
            ],
            {},
            None,
        ))

        new_app_cache = project_state.render()
        self.assertEqual(new_app_cache.get_model("migrations", "Tag")._meta.get_field_by_name("name")[0].max_length, 100)
        self.assertEqual(new_app_cache.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False)
Example #45
0
    def test_custom_manager_swappable(self):
        """
        Tests making a ProjectState from unused models with custom managers
        """
        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)

        # The default manager is used in migrations
        self.assertEqual([name for name, mgr in food_state.managers], ['food_mgr'])
        self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2))
Example #46
0
    def test_custom_manager_swappable(self):
        """
        Tests making a ProjectState from unused models with custom managers
        """
        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)

        # The default manager is used in migrations
        self.assertEqual([name for name, mgr in food_state.managers], ['food_mgr'])
        self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2))
Example #47
0
    def test_manager_refer_correct_model_version(self):
        """
        #24147 - Tests that managers refer to the correct version of a
        historical model
        """
        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)
Example #48
0
class AutodetectorTests(TestCase):
    """
    Tests the migration autodetector.
    """

    author_empty = ModelState("testapp", "Author",
                              [("id", models.AutoField(primary_key=True))])
    author_name = ModelState("testapp", "Author",
                             [("id", models.AutoField(primary_key=True)),
                              ("name", models.CharField(max_length=200))])
    author_name_longer = ModelState(
        "testapp", "Author", [("id", models.AutoField(primary_key=True)),
                              ("name", models.CharField(max_length=400))])
    author_name_renamed = ModelState(
        "testapp", "Author", [("id", models.AutoField(primary_key=True)),
                              ("names", models.CharField(max_length=200))])
    author_with_book = ModelState(
        "testapp", "Author", [("id", models.AutoField(primary_key=True)),
                              ("name", models.CharField(max_length=200)),
                              ("book", models.ForeignKey("otherapp.Book"))])
    author_with_publisher = ModelState(
        "testapp", "Author",
        [("id", models.AutoField(primary_key=True)),
         ("name", models.CharField(max_length=200)),
         ("publisher", models.ForeignKey("testapp.Publisher"))])
    author_proxy = ModelState("testapp", "AuthorProxy", [], {"proxy": True},
                              ("testapp.author", ))
    author_proxy_notproxy = ModelState("testapp", "AuthorProxy", [], {},
                                       ("testapp.author", ))
    publisher = ModelState("testapp", "Publisher",
                           [("id", models.AutoField(primary_key=True)),
                            ("name", models.CharField(max_length=100))])
    publisher_with_author = ModelState(
        "testapp", "Publisher",
        [("id", models.AutoField(primary_key=True)),
         ("author", models.ForeignKey("testapp.Author")),
         ("name", models.CharField(max_length=100))])
    other_pony = ModelState("otherapp", "Pony",
                            [("id", models.AutoField(primary_key=True))])
    other_stable = ModelState("otherapp", "Stable",
                              [("id", models.AutoField(primary_key=True))])
    third_thing = ModelState("thirdapp", "Thing",
                             [("id", models.AutoField(primary_key=True))])
    book = ModelState("otherapp", "Book",
                      [("id", models.AutoField(primary_key=True)),
                       ("author", models.ForeignKey("testapp.Author")),
                       ("title", models.CharField(max_length=200))])
    book_unique = ModelState("otherapp", "Book",
                             [("id", models.AutoField(primary_key=True)),
                              ("author", models.ForeignKey("testapp.Author")),
                              ("title", models.CharField(max_length=200))],
                             {"unique_together": [("author", "title")]})
    book_unique_2 = ModelState(
        "otherapp", "Book", [("id", models.AutoField(primary_key=True)),
                             ("author", models.ForeignKey("testapp.Author")),
                             ("title", models.CharField(max_length=200))],
        {"unique_together": [("title", "author")]})
    edition = ModelState("thirdapp", "Edition",
                         [("id", models.AutoField(primary_key=True)),
                          ("book", models.ForeignKey("otherapp.Book"))])

    def make_project_state(self, model_states):
        "Shortcut to make ProjectStates from lists of predefined models"
        project_state = ProjectState()
        for model_state in model_states:
            project_state.add_model_state(model_state.clone())
        return project_state

    def test_arrange_for_graph(self):
        "Tests auto-naming of migrations for graph matching."
        # Make a fake graph
        graph = MigrationGraph()
        graph.add_node(("testapp", "0001_initial"), None)
        graph.add_node(("testapp", "0002_foobar"), None)
        graph.add_node(("otherapp", "0001_initial"), None)
        graph.add_dependency(("testapp", "0002_foobar"),
                             ("testapp", "0001_initial"))
        graph.add_dependency(("testapp", "0002_foobar"),
                             ("otherapp", "0001_initial"))
        # Use project state to make a new migration change set
        before = self.make_project_state([])
        after = self.make_project_state(
            [self.author_empty, self.other_pony, self.other_stable])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Run through arrange_for_graph
        changes = autodetector._arrange_for_graph(changes, graph)
        # Make sure there's a new name, deps match, etc.
        self.assertEqual(changes["testapp"][0].name, "0003_author")
        self.assertEqual(changes["testapp"][0].dependencies,
                         [("testapp", "0002_foobar")])
        self.assertEqual(changes["otherapp"][0].name, "0002_pony_stable")
        self.assertEqual(changes["otherapp"][0].dependencies,
                         [("otherapp", "0001_initial")])

    def test_trim_apps(self):
        "Tests that trim does not remove dependencies but does remove unwanted apps"
        # Use project state to make a new migration change set
        before = self.make_project_state([])
        after = self.make_project_state([
            self.author_empty, self.other_pony, self.other_stable,
            self.third_thing
        ])
        autodetector = MigrationAutodetector(
            before, after, MigrationQuestioner({"ask_initial": True}))
        changes = autodetector._detect_changes()
        # Run through arrange_for_graph
        graph = MigrationGraph()
        changes = autodetector._arrange_for_graph(changes, graph)
        changes["testapp"][0].dependencies.append(("otherapp", "0001_initial"))
        changes = autodetector._trim_to_apps(changes, set(["testapp"]))
        # Make sure there's the right set of migrations
        self.assertEqual(changes["testapp"][0].name, "0001_initial")
        self.assertEqual(changes["otherapp"][0].name, "0001_initial")
        self.assertNotIn("thirdapp", changes)

    def test_new_model(self):
        "Tests autodetection of new models"
        # Make state
        before = self.make_project_state([])
        after = self.make_project_state([self.author_empty])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        self.assertEqual(action.name, "Author")

    def test_old_model(self):
        "Tests deletion of old models"
        # Make state
        before = self.make_project_state([self.author_empty])
        after = self.make_project_state([])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "DeleteModel")
        self.assertEqual(action.name, "Author")

    def test_add_field(self):
        "Tests autodetection of new fields"
        # Make state
        before = self.make_project_state([self.author_empty])
        after = self.make_project_state([self.author_name])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "AddField")
        self.assertEqual(action.name, "name")

    def test_remove_field(self):
        "Tests autodetection of removed fields"
        # Make state
        before = self.make_project_state([self.author_name])
        after = self.make_project_state([self.author_empty])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "RemoveField")
        self.assertEqual(action.name, "name")

    def test_alter_field(self):
        "Tests autodetection of new fields"
        # Make state
        before = self.make_project_state([self.author_name])
        after = self.make_project_state([self.author_name_longer])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "AlterField")
        self.assertEqual(action.name, "name")

    def test_rename_field(self):
        "Tests autodetection of renamed fields"
        # Make state
        before = self.make_project_state([self.author_name])
        after = self.make_project_state([self.author_name_renamed])
        autodetector = MigrationAutodetector(
            before, after, MigrationQuestioner({"ask_rename": True}))
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "RenameField")
        self.assertEqual(action.old_name, "name")
        self.assertEqual(action.new_name, "names")

    def test_fk_dependency(self):
        "Tests that having a ForeignKey automatically adds a dependency"
        # Make state
        before = self.make_project_state([])
        after = self.make_project_state(
            [self.author_name, self.book, self.edition])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        self.assertEqual(len(changes['otherapp']), 1)
        self.assertEqual(len(changes['thirdapp']), 1)
        # Right number of actions?
        migration1 = changes['testapp'][0]
        self.assertEqual(len(migration1.operations), 1)
        migration2 = changes['otherapp'][0]
        self.assertEqual(len(migration2.operations), 1)
        migration3 = changes['thirdapp'][0]
        self.assertEqual(len(migration3.operations), 1)
        # Right actions?
        action = migration1.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        action = migration2.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        action = migration3.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        # Right dependencies?
        self.assertEqual(migration1.dependencies, [])
        self.assertEqual(migration2.dependencies, [("testapp", "auto_1")])
        self.assertEqual(migration3.dependencies, [("otherapp", "auto_1")])

    def test_same_app_no_fk_dependency(self):
        """
        Tests that a migration with a FK between two models of the same app
        does not have a dependency to itself.
        """
        # Make state
        before = self.make_project_state([])
        after = self.make_project_state(
            [self.author_with_publisher, self.publisher])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 2)
        # Right actions?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        action = migration.operations[1]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        # Right dependencies?
        self.assertEqual(migration.dependencies, [])

    def test_circular_fk_dependency(self):
        """
        Tests that having a circular ForeignKey dependency automatically
        resolves the situation into 2 migrations on one side and 1 on the other.
        """
        # Make state
        before = self.make_project_state([])
        after = self.make_project_state([self.author_with_book, self.book])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        self.assertEqual(len(changes['otherapp']), 2)
        # Right number of actions?
        migration1 = changes['testapp'][0]
        self.assertEqual(len(migration1.operations), 1)
        migration2 = changes['otherapp'][0]
        self.assertEqual(len(migration2.operations), 1)
        migration3 = changes['otherapp'][1]
        self.assertEqual(len(migration2.operations), 1)
        # Right actions?
        action = migration1.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        action = migration2.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        self.assertEqual(len(action.fields), 2)
        action = migration3.operations[0]
        self.assertEqual(action.__class__.__name__, "AddField")
        self.assertEqual(action.name, "author")
        # Right dependencies?
        self.assertEqual(migration1.dependencies, [("otherapp", "auto_1")])
        self.assertEqual(migration2.dependencies, [])
        self.assertEqual(set(migration3.dependencies),
                         set([("otherapp", "auto_1"), ("testapp", "auto_1")]))

    def test_same_app_circular_fk_dependency(self):
        """
        Tests that a migration with a FK between two models of the same app
        does not have a dependency to itself.
        """
        # Make state
        before = self.make_project_state([])
        after = self.make_project_state(
            [self.author_with_publisher, self.publisher_with_author])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 2)
        # Right number of actions?
        migration1 = changes['testapp'][0]
        self.assertEqual(len(migration1.operations), 2)
        migration2 = changes['testapp'][1]
        self.assertEqual(len(migration2.operations), 1)
        # Right actions?
        action = migration1.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        action = migration1.operations[1]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        action = migration2.operations[0]
        self.assertEqual(action.__class__.__name__, "AddField")
        self.assertEqual(action.name, "publisher")
        # Right dependencies?
        self.assertEqual(migration1.dependencies, [])
        self.assertEqual(migration2.dependencies, [("testapp", "auto_1")])

    def test_unique_together(self):
        "Tests unique_together detection"
        # Make state
        before = self.make_project_state([self.author_empty, self.book])
        after = self.make_project_state([self.author_empty, self.book_unique])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['otherapp']), 1)
        # Right number of actions?
        migration = changes['otherapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "AlterUniqueTogether")
        self.assertEqual(action.name, "book")
        self.assertEqual(action.unique_together, set([("author", "title")]))

    def test_unique_together_ordering(self):
        "Tests that unique_together also triggers on ordering changes"
        # Make state
        before = self.make_project_state([self.author_empty, self.book_unique])
        after = self.make_project_state(
            [self.author_empty, self.book_unique_2])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['otherapp']), 1)
        # Right number of actions?
        migration = changes['otherapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "AlterUniqueTogether")
        self.assertEqual(action.name, "book")
        self.assertEqual(action.unique_together, set([("title", "author")]))

    def test_proxy_ignorance(self):
        "Tests that the autodetector correctly ignores proxy models"
        # First, we test adding a proxy model
        before = self.make_project_state([self.author_empty])
        after = self.make_project_state([self.author_empty, self.author_proxy])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes), 0)

        # Now, we test turning a proxy model into a non-proxy model
        before = self.make_project_state(
            [self.author_empty, self.author_proxy])
        after = self.make_project_state(
            [self.author_empty, self.author_proxy_notproxy])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        # Right number of actions?
        migration = changes['testapp'][0]
        self.assertEqual(len(migration.operations), 1)
        # Right action?
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "CreateModel")
        self.assertEqual(action.name, "AuthorProxy")
Example #49
0
    def _migrate(self, plan, connection, executor, pre_migrate_state, targets,
                 options):
        if self.verbosity >= 1:
            self.stdout.write(
                self.style.MIGRATE_HEADING("Running migrations:"))
        if not plan:
            if self.verbosity >= 1:
                self.stdout.write("  No migrations to apply.")
                # If there's changes that aren't in migrations yet, tell them how to fix it.
                autodetector = MigrationAutodetector(
                    executor.loader.project_state(),
                    ProjectState.from_apps(apps),
                )
                changes = autodetector.changes(graph=executor.loader.graph)
                if changes:
                    self.stdout.write(
                        self.style.NOTICE(
                            "  Your models have changes that are not yet reflected "
                            "in a migration, and so won't be applied."))
                    self.stdout.write(
                        self.style.NOTICE(
                            "  Run 'manage.py makemigrations' to make new "
                            "migrations, and then re-run 'manage.py migrate' to "
                            "apply them."))
            fake = False
            fake_initial = False
        else:
            fake = options['fake']
            fake_initial = options['fake_initial']
        post_migrate_state = executor.migrate(
            targets,
            plan=plan,
            state=pre_migrate_state.clone(),
            fake=fake,
            fake_initial=fake_initial,
        )
        # post_migrate signals have access to all models. Ensure that all models
        # are reloaded in case any are delayed.
        post_migrate_state.clear_delayed_apps_cache()
        post_migrate_apps = post_migrate_state.apps

        # Re-render models of real apps to include relationships now that
        # we've got a final state. This wouldn't be necessary if real apps
        # models were rendered with relationships in the first place.
        with post_migrate_apps.bulk_update():
            model_keys = []
            for model_state in post_migrate_apps.real_models:
                model_key = model_state.app_label, model_state.name_lower
                model_keys.append(model_key)
                post_migrate_apps.unregister_model(*model_key)
        post_migrate_apps.render_multiple([
            ModelState.from_model(apps.get_model(*model))
            for model in model_keys
        ])

        # Send the post_migrate signal, so individual apps can do whatever they need
        # to do at this point.
        emit_post_migrate_signal(
            self.verbosity,
            self.interactive,
            connection.alias,
            apps=post_migrate_apps,
            plan=plan,
        )
Example #50
0
    def handle(self, *args, **options):

        self.verbosity = options['verbosity']
        self.interactive = options['interactive']

        # Import the 'management' module within each installed app, to register
        # dispatcher events.
        for app_config in apps.get_app_configs():
            if module_has_submodule(app_config.module, "management"):
                import_module('.management', app_config.name)

        # Get the database we're operating from
        db = options['database']
        connection = connections[db]

        # Hook for backends needing any database preparation
        connection.prepare_database()
        # Work out which apps have migrations and which do not
        executor = MigrationExecutor(connection, self.migration_progress_callback)

        # Raise an error if any migrations are applied before their dependencies.
        executor.loader.check_consistent_history(connection)

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any
        conflicts = executor.loader.detect_conflicts()
        if conflicts:
            name_str = "; ".join(
                "%s in %s" % (", ".join(names), app)
                for app, names in conflicts.items()
            )
            raise CommandError(
                "Conflicting migrations detected; multiple leaf nodes in the "
                "migration graph: (%s).\nTo fix them run "
                "'python manage.py makemigrations --merge'" % name_str
            )

        # If they supplied command line arguments, work out what they mean.
        target_app_labels_only = True
        if options['app_label']:
            # Validate app_label.
            app_label = options['app_label']
            try:
                apps.get_app_config(app_label)
            except LookupError as err:
                raise CommandError(str(err))
            if app_label not in executor.loader.migrated_apps:
                raise CommandError("App '%s' does not have migrations." % app_label)

        if options['app_label'] and options['migration_name']:
            migration_name = options['migration_name']
            if migration_name == "zero":
                targets = [(app_label, None)]
            else:
                try:
                    migration = executor.loader.get_migration_by_prefix(app_label, migration_name)
                except AmbiguityError:
                    raise CommandError(
                        "More than one migration matches '%s' in app '%s'. "
                        "Please be more specific." %
                        (migration_name, app_label)
                    )
                except KeyError:
                    raise CommandError("Cannot find a migration matching '%s' from app '%s'." % (
                        migration_name, app_label))
                targets = [(app_label, migration.name)]
            target_app_labels_only = False
        elif options['app_label']:
            targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label]
        else:
            targets = executor.loader.graph.leaf_nodes()

        plan = executor.migration_plan(targets)

        if options['plan']:
            self.stdout.write('Planned operations:', self.style.MIGRATE_LABEL)
            if not plan:
                self.stdout.write('  No planned migration operations.')
            for migration, backwards in plan:
                self.stdout.write(str(migration), self.style.MIGRATE_HEADING)
                for operation in migration.operations:
                    message, is_error = self.describe_operation(operation, backwards)
                    style = self.style.WARNING if is_error else None
                    self.stdout.write('    ' + message, style)
            return

        run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps
        # Print some useful info
        if self.verbosity >= 1:
            self.stdout.write(self.style.MIGRATE_HEADING("Operations to perform:"))
            if run_syncdb:
                self.stdout.write(
                    self.style.MIGRATE_LABEL("  Synchronize unmigrated apps: ") +
                    (", ".join(sorted(executor.loader.unmigrated_apps)))
                )
            if target_app_labels_only:
                self.stdout.write(
                    self.style.MIGRATE_LABEL("  Apply all migrations: ") +
                    (", ".join(sorted({a for a, n in targets})) or "(none)")
                )
            else:
                if targets[0][1] is None:
                    self.stdout.write(self.style.MIGRATE_LABEL(
                        "  Unapply all migrations: ") + "%s" % (targets[0][0],)
                    )
                else:
                    self.stdout.write(self.style.MIGRATE_LABEL(
                        "  Target specific migration: ") + "%s, from %s"
                        % (targets[0][1], targets[0][0])
                    )

        pre_migrate_state = executor._create_project_state(with_applied_migrations=True)
        pre_migrate_apps = pre_migrate_state.apps
        emit_pre_migrate_signal(
            self.verbosity, self.interactive, connection.alias, apps=pre_migrate_apps, plan=plan,
        )

        # Run the syncdb phase.
        if run_syncdb:
            if self.verbosity >= 1:
                self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:"))
            self.sync_apps(connection, executor.loader.unmigrated_apps)

        # Migrate!
        if self.verbosity >= 1:
            self.stdout.write(self.style.MIGRATE_HEADING("Running migrations:"))
        if not plan:
            if self.verbosity >= 1:
                self.stdout.write("  No migrations to apply.")
                # If there's changes that aren't in migrations yet, tell them how to fix it.
                autodetector = MigrationAutodetector(
                    executor.loader.project_state(),
                    ProjectState.from_apps(apps),
                )
                changes = autodetector.changes(graph=executor.loader.graph)
                if changes:
                    self.stdout.write(self.style.NOTICE(
                        "  Your models have changes that are not yet reflected "
                        "in a migration, and so won't be applied."
                    ))
                    self.stdout.write(self.style.NOTICE(
                        "  Run 'manage.py makemigrations' to make new "
                        "migrations, and then re-run 'manage.py migrate' to "
                        "apply them."
                    ))
            fake = False
            fake_initial = False
        else:
            fake = options['fake']
            fake_initial = options['fake_initial']
        post_migrate_state = executor.migrate(
            targets, plan=plan, state=pre_migrate_state.clone(), fake=fake,
            fake_initial=fake_initial,
        )
        # post_migrate signals have access to all models. Ensure that all models
        # are reloaded in case any are delayed.
        post_migrate_state.clear_delayed_apps_cache()
        post_migrate_apps = post_migrate_state.apps

        # Re-render models of real apps to include relationships now that
        # we've got a final state. This wouldn't be necessary if real apps
        # models were rendered with relationships in the first place.
        with post_migrate_apps.bulk_update():
            model_keys = []
            for model_state in post_migrate_apps.real_models:
                model_key = model_state.app_label, model_state.name_lower
                model_keys.append(model_key)
                post_migrate_apps.unregister_model(*model_key)
        post_migrate_apps.render_multiple([
            ModelState.from_model(apps.get_model(*model)) for model in model_keys
        ])

        # Send the post_migrate signal, so individual apps can do whatever they need
        # to do at this point.
        emit_post_migrate_signal(
            self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan,
        )
Example #51
0
 def get_model_state(cls, **kwargs):
     return ModelState.from_model(cls, **kwargs)
Example #52
0
 def test_custom_model_base(self):
     state = ModelState.from_model(ModelWithCustomBase)
     self.assertEqual(state.bases, (models.Model,))
Example #53
0
def _get_state(models):
    project_state = ProjectState.from_apps(apps)
    for model in models:
        model_state = ModelState.from_model(model)
        project_state.add_model(model_state)
    return project_state
Example #54
0
    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

        # Make a valid ProjectState and render it
        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)

        # now make an invalid one with a ForeignKey
        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

        # And another with ManyToManyField.
        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

        # And now with multiple models and multiple fields.
        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
Example #55
0
    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

        # Make a valid ProjectState and render it
        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)

        # now make an invalid one with a ForeignKey
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "Unhandled pending operations for models:\n"
            "  migrations.author (referred to by fields: migrations.Book.author)\n"
            "  migrations.publisher (referred to by fields: migrations.Book.publisher)"
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        # And another with ManyToManyField.
        project_state = ProjectState()
        project_state.add_model(ModelState.from_model(Magazine))
        msg = (
            "Unhandled pending operations for models:\n"
            "  migrations.author (referred to by fields: "
            "migrations.Magazine.authors, migrations.Magazine_authors.author)"
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps

        # And now with multiple models and multiple fields.
        project_state.add_model(ModelState.from_model(Book))
        msg = (
            "Unhandled pending operations for models:\n"
            "  migrations.author (referred to by fields: migrations.Book.author, "
            "migrations.Magazine.authors, migrations.Magazine_authors.author)\n"
            "  migrations.publisher (referred to by fields: migrations.Book.publisher)"
        )
        with self.assertRaisesMessage(ValueError, msg):
            project_state.apps
Example #56
0
    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"])

        # We shouldn't be able to render yet
        ms = ModelState.from_model(FooBar)
        with self.assertRaises(InvalidBasesError):
            ms.render(apps)

        # Once the parent models are in the app registry, it should be fine
        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'])
Example #57
0
 def build_graph(self):
     """
     Builds a migration dependency graph using both the disk and database.
     You'll need to rebuild the graph if you apply migrations. This isn't
     usually a problem as generally migration stuff runs in a one-shot process.
     """
     # Load disk data
     self.load_disk()
     # Load database data
     recorder = MigrationRecorder(self.connection)
     self.applied_migrations = recorder.applied_migrations()
     # Do a first pass to separate out replacing and non-replacing migrations
     normal = {}
     replacing = {}
     for key, migration in self.disk_migrations.items():
         if migration.replaces:
             replacing[key] = migration
         else:
             normal[key] = migration
     # Calculate reverse dependencies - i.e., for each migration, what depends on it?
     # This is just for dependency re-pointing when applying replacements,
     # so we ignore run_before here.
     reverse_dependencies = {}
     for key, migration in normal.items():
         for parent in migration.dependencies:
             reverse_dependencies.setdefault(parent, set()).add(key)
     # Carry out replacements if we can - that is, if all replaced migrations
     # are either unapplied or missing.
     for key, migration in replacing.items():
         # Ensure this replacement migration is not in applied_migrations
         self.applied_migrations.discard(key)
         # Do the check. We can replace if all our replace targets are
         # applied, or if all of them are unapplied.
         applied_statuses = [(target in self.applied_migrations)
                             for target in migration.replaces]
         can_replace = all(applied_statuses) or (not any(applied_statuses))
         if not can_replace:
             continue
         # Alright, time to replace. Step through the replaced migrations
         # and remove, repointing dependencies if needs be.
         for replaced in migration.replaces:
             if replaced in normal:
                 # We don't care if the replaced migration doesn't exist;
                 # the usage pattern here is to delete things after a while.
                 del normal[replaced]
             for child_key in reverse_dependencies.get(replaced, set()):
                 if child_key in migration.replaces:
                     continue
                 normal[child_key].dependencies.remove(replaced)
                 normal[child_key].dependencies.append(key)
         normal[key] = migration
         # Mark the replacement as applied if all its replaced ones are
         if all(applied_statuses):
             self.applied_migrations.add(key)
     # Finally, make a graph and load everything into it
     self.graph = MigrationGraph()
     for key, migration in normal.items():
         self.graph.add_node(key, migration)
     for key, migration in normal.items():
         for parent in migration.dependencies:
             # Special-case __first__, which means "the first migration" for
             # migrated apps, and is ignored for unmigrated apps. It allows
             # makemigrations to declare dependencies on apps before they
             # even have migrations.
             if parent[1] == "__first__" and parent not in self.graph:
                 if parent[0] in self.unmigrated_apps:
                     # This app isn't migrated, but something depends on it.
                     # We'll add a fake initial migration for it into the
                     # graph.
                     app_config = apps.get_app_config(parent[0])
                     ops = []
                     for model in app_config.get_models():
                         model_state = ModelState.from_model(model)
                         ops.append(
                             operations.CreateModel(
                                 name=model_state.name,
                                 fields=model_state.fields,
                                 options=model_state.options,
                                 bases=model_state.bases,
                             ))
                     new_migration = type(
                         "FakeInitialMigration",
                         (Migration, ),
                         {"operations": ops},
                     )(parent[1], parent[0])
                     self.graph.add_node(parent, new_migration)
                     self.applied_migrations.add(parent)
                 elif parent[0] in self.migrated_apps:
                     parent = (parent[0],
                               list(self.graph.root_nodes(parent[0]))[0])
                 else:
                     raise ValueError("Dependency on unknown app %s" %
                                      parent[0])
             self.graph.add_dependency(key, parent)
Example #58
0
 def from_model(cls, model, exclude_rels=False):
     result = ModelState.from_model(model, exclude_rels=exclude_rels)
     if getattr(model._meta, '_is_view', False):
         result.options['db_table'] = model._meta._base_model._meta.db_table
     return result