Beispiel #1
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,
        )
 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())
Beispiel #3
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"],
        )
Beispiel #4
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)

            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_state(ModelState.from_model(Author))
        project_state.add_model_state(ModelState.from_model(Book))
        self.assertEqual(
            [
                name for name, field in project_state.models["migrations",
                                                             "book"].fields
            ],
            ["id", "author"],
        )
Beispiel #5
0
    def test_add_relations(self):
        """
        #24573 - Adding relations to existing models should reload the
        referenced models too.
        """
        class A(models.Model):
            class Meta:
                app_label = 'something'

        class B(A):
            class Meta:
                app_label = 'something'

        class C(models.Model):
            class Meta:
                app_label = 'something'

        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', 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)
Beispiel #6
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")

            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_state(ModelState.from_model(TestModel))
        with self.assertRaises(ValueError):
            project_state.render()

        # If we include the real app it should succeed
        project_state = ProjectState(real_apps=["contenttypes"])
        project_state.add_model_state(ModelState.from_model(TestModel))
        rendered_state = project_state.render()
        self.assertEqual(
            len([
                x for x in rendered_state.get_models()
                if x._meta.app_label == "migrations"
            ]),
            1,
        )
Beispiel #7
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()
Beispiel #8
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)
Beispiel #9
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
Beispiel #10
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_state(ModelState.from_model(A))
        project_state.add_model_state(ModelState.from_model(B))
        project_state.add_model_state(ModelState.from_model(C))
        project_state.add_model_state(ModelState.from_model(D))
        project_state.add_model_state(ModelState.from_model(E))
        project_state.add_model_state(ModelState.from_model(F))
        final_apps = project_state.render()
        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_state(ModelState.from_model(A))
        project_state.add_model_state(ModelState.from_model(B))
        project_state.add_model_state(ModelState.from_model(C))
        project_state.add_model_state(ModelState.from_model(F))
        with self.assertRaises(InvalidBasesError):
            project_state.render()
Beispiel #11
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)
Beispiel #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.
        """
        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)
Beispiel #13
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)
Beispiel #14
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.assertEqual(author_state.fields[2][1].null, False)
        self.assertEqual(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, [])
Beispiel #15
0
def migrate():
    """Migrate the database.

    This is a simplified version of Django's ``migrate`` management command
    that does not require ``management.call_command`` which is not functional
    in the native binary environment.
    """
    # Import the 'management' module within each installed app, to register
    # dispatcher events.
    verbosity = int(settings.DEBUG)
    interactive = False
    for app_config in apps.get_app_configs():
        if module_has_submodule(app_config.module, "management"):
            import_module(".management", app_config.name)

    connection = connections[DEFAULT_DB_ALIAS]
    connection.prepare_database()
    executor = MigrationExecutor(connection, migration_progress_callback)
    executor.loader.check_consistent_history(connection)
    conflicts = executor.loader.detect_conflicts()
    if conflicts:
        raise Exception("Conflicting mgirations detected.")
    targets = executor.loader.graph.leaf_nodes()
    plan = executor.migration_plan(targets)
    pre_migrate_state = executor._create_project_state(
        with_applied_migrations=True)
    pre_migrate_apps = pre_migrate_state.apps
    emit_pre_migrate_signal(
        verbosity,
        False,
        connection.alias,
        apps=pre_migrate_apps,
        plan=plan,
    )
    post_migrate_state = executor.migrate(
        targets,
        plan=plan,
        state=pre_migrate_state.clone(),
        fake=False,
        fake_initial=False,
    )
    post_migrate_state.clear_delayed_apps_cache()
    post_migrate_apps = post_migrate_state.apps
    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
    ])
    emit_post_migrate_signal(
        verbosity,
        interactive,
        connection.alias,
        apps=post_migrate_apps,
        plan=plan,
    )
    logger.info("Database configured.")
Beispiel #16
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, [])
def finalize_migrations(post_migrate_state):
    """Finalize any migrations operations.

    This will update any internal state in Django for any migrations that
    were applied and represented by the provided post-migrate state.

    Args:
        post_migrate_state (object):
            The state generated from applying migrations. This must be the
            result of :py:meth:`apply_migrations`.
    """
    assert supports_migrations, \
        'This cannot be called on Django 1.6 or earlier.'

    if django_version >= (1, 10):
        # On Django 1.10, we have a few more steps for generating the state
        # needed for the signal.
        if django_version >= (1, 11):
            post_migrate_state.clear_delayed_apps_cache()

        post_migrate_apps = post_migrate_state.apps
        assert post_migrate_apps is not None

        model_keys = []

        with post_migrate_apps.bulk_update():
            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(get_model(*model))
            for model in model_keys
        ])
Beispiel #18
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')})
Beispiel #19
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)
Beispiel #20
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)
Beispiel #21
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)
Beispiel #22
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)
Beispiel #23
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
Beispiel #24
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')})
Beispiel #25
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
Beispiel #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)
Beispiel #27
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()
Beispiel #28
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)
Beispiel #29
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
Beispiel #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)
Beispiel #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)
Beispiel #32
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())
Beispiel #33
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)
Beispiel #34
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)
Beispiel #35
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')})
Beispiel #36
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"))
Beispiel #37
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')})
Beispiel #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)
Beispiel #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)
Beispiel #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)
Beispiel #41
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)
Beispiel #42
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))
Beispiel #43
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))
Beispiel #44
0
 def build_graph(self, ignore_unmigrated=False):
     """
     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:
                     if ignore_unmigrated:
                         parent = None
                     else:
                         # 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 = list(self.graph.root_nodes(parent[0]))[0]
                 else:
                     raise ValueError("Dependency on unknown app %s" % parent[0])
             if parent is not None:
                 self.graph.add_dependency(key, parent)
Beispiel #45
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'])
Beispiel #46
0
 def get_model_state(cls, **kwargs):
     return ModelState.from_model(cls, **kwargs)
Beispiel #47
0
 def test_custom_model_base(self):
     state = ModelState.from_model(ModelWithCustomBase)
     self.assertEqual(state.bases, (models.Model,))
Beispiel #48
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
Beispiel #49
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'])
    def handle(self, *args, **options):

        self.branch = options['branch']
        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)

        targets = []
        migrations = {}
        migration_paths = self.get_diff_migrations()

        # Group diff migrations by app_label
        for p in migration_paths:
            app_label = p.parts[0]
            if app_label not in migrations:
                # Validate app_label.
                try:
                    apps.get_app_config(app_label)
                except LookupError as err:
                    raise CommandError(str(err))
                migrations[app_label] = []
            migrations[app_label].append(p.stem)

        # Get last applied migration for each app
        for app_label, migration_names in migrations.items():
            mr = MigrationRecorder.Migration.objects.filter(app=app_label)

            # Skip if already unapplied
            if not mr.filter(name__in=migration_names).count():
                continue

            mr = mr.exclude(
                name__in=migration_names).order_by('applied').last()
            if mr:
                targets.append((app_label, mr.name))

        if not targets:
            if self.verbosity >= 1:
                self.stdout.write("  No migrations to unapply.")
            return

        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

        # Print some useful info
        if self.verbosity >= 1:
            self.stdout.write(
                self.style.MIGRATE_HEADING("Operations to perform:"))
            for app_label, migration_name in targets:
                self.stdout.write(
                    self.style.MIGRATE_LABEL("  Target specific migration: ") +
                    "%s, from %s" % (app_label, migration_name))

        # noinspection PyProtectedMember
        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,
        )

        # Migrate!
        if self.verbosity >= 1:
            self.stdout.write(
                self.style.MIGRATE_HEADING("Running migrations:"))

        if not plan:
            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,
        )
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
Beispiel #52
0
    def handle(self, *args, **options):
        database = options['database']
        if not options['skip_checks']:
            self.check(databases=[database])

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

        # Import the 'management' module within each installed BLOG, 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
        connection = connections[database]

        # 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.
        run_syncdb = options['run_syncdb']
        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 run_syncdb:
                if app_label in executor.loader.migrated_apps:
                    raise CommandError("Can't use run_syncdb with BLOG '%s' as it has migrations." % app_label)
            elif 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 BLOG '%s'. "
                        "Please be more specific." %
                        (migration_name, app_label)
                    )
                except KeyError:
                    raise CommandError("Cannot find a migration matching '%s' from BLOG '%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)
        exit_dry = plan and options['check_unapplied']

        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)
            if exit_dry:
                sys.exit(1)
            return
        if exit_dry:
            sys.exit(1)

        # At this point, ignore run_syncdb if there aren't any apps to sync.
        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:
                if options['app_label']:
                    self.stdout.write(
                        self.style.MIGRATE_LABEL("  Synchronize unmigrated BLOG: %s" % app_label)
                    )
                else:
                    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: ') +
                        str(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:"))
            if options['app_label']:
                self.sync_apps(connection, [app_label])
            else:
                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 in BLOG(s): %s have changes that are not "
                        "yet reflected in a migration, and so won't be "
                        "applied." % ", ".join(repr(app) for app in sorted(changes))
                    ))
                    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,
        )
Beispiel #53
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,
        )
Beispiel #54
0
 def test_custom_model_base(self):
     state = ModelState.from_model(ModelWithCustomBase)
     self.assertEqual(state.bases, (models.Model, ))
Beispiel #55
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,
        )
Beispiel #56
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
Beispiel #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)
Beispiel #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