def test_create_model(self):
     """
     Tests the CreateModel operation.
     Most other tests use this operation as part of setup, so check failures here first.
     """
     operation = migrations.CreateModel(
         "Pony",
         [
             ("id", models.AutoField(primary_key=True)),
             ("pink", models.IntegerField(default=1)),
         ],
     )
     # Test the state alteration
     project_state = ProjectState()
     new_state = project_state.clone()
     operation.state_forwards("test_crmo", new_state)
     self.assertEqual(new_state.models["test_crmo", "pony"].name, "Pony")
     self.assertEqual(len(new_state.models["test_crmo", "pony"].fields), 2)
     # Test the database alteration
     self.assertTableNotExists("test_crmo_pony")
     with connection.schema_editor() as editor:
         operation.database_forwards("test_crmo", editor, project_state, new_state)
     self.assertTableExists("test_crmo_pony")
     # And test reversal
     with connection.schema_editor() as editor:
         operation.database_backwards("test_crmo", editor, new_state, project_state)
     self.assertTableNotExists("test_crmo_pony")
     # And deconstruction
     definition = operation.deconstruct()
     self.assertEqual(definition[0], "CreateModel")
     self.assertEqual(len(definition[1]), 2)
     self.assertEqual(len(definition[2]), 0)
     self.assertEqual(definition[1][0], "Pony")
Exemple #2
0
    def test_manager_refer_correct_model_version(self):
        """
        #24147 - Tests that managers refer to the correct version of a
        historical model
        """
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="Tag",
                fields=[("id", models.AutoField(primary_key=True)), ("hidden", models.BooleanField())],
                managers=[("food_mgr", FoodManager("a", "b")), ("food_qs", FoodQuerySet.as_manager())],
            )
        )

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

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

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

        self.assertIsNot(old_model, new_model)
        self.assertIs(old_model, old_model.food_mgr.model)
        self.assertIs(old_model, old_model.food_qs.model)
        self.assertIs(new_model, new_model.food_mgr.model)
        self.assertIs(new_model, new_model.food_qs.model)
        self.assertIsNot(old_model.food_mgr, new_model.food_mgr)
        self.assertIsNot(old_model.food_qs, new_model.food_qs)
        self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model)
        self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model)
    def test_install_plugin(self):
        """
        Test we can load the example plugin that every version of MySQL ships
        with.
        """
        self.assertPluginNotExists("metadata_lock_info")

        state = ProjectState()
        operation = InstallPlugin("metadata_lock_info",
                                  "metadata_lock_info.so")
        self.assertEqual(
            operation.describe(),
            "Installs plugin metadata_lock_info from metadata_lock_info.so"
        )
        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_forwards("django_mysql_tests", editor,
                                        state, new_state)

        self.assertPluginExists("metadata_lock_info")

        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_backwards("django_mysql_tests", editor,
                                         new_state, state)

        self.assertPluginNotExists("metadata_lock_info")
 def test_create_model(self):
     """
     Tests the CreateModel operation.
     """
     operation = migrations.CreateModel(
         "uuidmodel",
         [
             ("id", models.AutoField(primary_key=True)),
             ("uuid", UUIDField(auto=True)),
             ],
         )
     # Test the state alteration
     project_state = ProjectState()
     new_state = project_state.clone()
     operation.state_forwards("test_crmo", new_state)
     self.assertEqual(new_state.models["test_crmo", "uuidmodel"].name, "uuidmodel")
     self.assertEqual(len(new_state.models["test_crmo", "uuidmodel"].fields), 2)
     # Test the database alteration
     self.assertTableNotExists("test_crmo_uuidmodel")
     with connection.schema_editor() as editor:
         operation.database_forwards("test_crmo", editor, project_state, new_state)
     self.assertTableExists("test_crmo_uuidmodel")
     self.assertColumnExists("test_crmo_uuidmodel", "uuid")
     # And test reversal
     with connection.schema_editor() as editor:
         operation.database_backwards("test_crmo", editor, new_state, project_state)
     self.assertTableNotExists("test_crmo_uuidmodel")
     # And deconstruction
     definition = operation.deconstruct()
     self.assertEqual(definition[0], "CreateModel")
     self.assertEqual(len(definition[1]), 2)
     self.assertEqual(len(definition[2]), 0)
     self.assertEqual(definition[1][0], "uuidmodel")
Exemple #5
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"],
        )
    def test_install_plugin(self):
        """
        Test we can load the example plugin that every version of MySQL ships
        with.
        """
        assert not plugin_exists("metadata_lock_info")

        state = ProjectState()
        operation = InstallPlugin("metadata_lock_info",
                                  "metadata_lock_info.so")
        assert (
            operation.describe() ==
            "Installs plugin metadata_lock_info from metadata_lock_info.so"
        )
        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_forwards("testapp", editor,
                                        state, new_state)

        assert plugin_exists("metadata_lock_info")

        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_backwards("testapp", editor,
                                         new_state, state)

        assert not plugin_exists("metadata_lock_info")
Exemple #7
0
    def test_repr(self):
        field = models.CharField(max_length=1)
        state = ModelState('app', 'Model', [('name', field)], bases=['app.A', 'app.B', 'app.C'])
        self.assertEqual(repr(state), "<ModelState: 'app.Model'>")

        project_state = ProjectState()
        project_state.add_model(state)
        with self.assertRaisesMessage(InvalidBasesError, "Cannot resolve bases for [<ModelState: 'app.Model'>]"):
            project_state.apps
Exemple #8
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,
        )
Exemple #9
0
    def test_render(self):
        """
        Tests rendering a ProjectState into an Apps.
        """
        project_state = ProjectState()
        project_state.add_model_state(ModelState(
            app_label="migrations",
            name="Tag",
            fields=[
                ("id", models.AutoField(primary_key=True)),
                ("name", models.CharField(max_length=100)),
                ("hidden", models.BooleanField()),
            ],
        ))
        project_state.add_model_state(ModelState(
            app_label="migrations",
            name="SubTag",
            fields=[
                ('tag_ptr', models.OneToOneField(
                    auto_created=True,
                    primary_key=True,
                    to_field='id',
                    serialize=False,
                    to='migrations.Tag',
                )),
                ("awesome", models.BooleanField()),
            ],
            bases=("migrations.Tag",),
        ))

        base_mgr = models.Manager()
        mgr1 = FoodManager('a', 'b')
        mgr2 = FoodManager('x', 'y', c=3, d=4)
        project_state.add_model_state(ModelState(
            app_label="migrations",
            name="Food",
            fields=[
                ("id", models.AutoField(primary_key=True)),
            ],
            managers=[
                # The ordering we really want is objects, mgr1, mgr2
                ('default', base_mgr),
                ('food_mgr2', mgr2),
                ('food_mgr1', mgr1),
            ]
        ))

        new_apps = project_state.render()
        self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("name")[0].max_length, 100)
        self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False)

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

        Food = new_apps.get_model("migrations", "Food")
        managers = sorted(Food._meta.managers)
        self.assertEqual([mgr.name for _, mgr, _ in managers],
                         ['default', 'food_mgr1', 'food_mgr2'])
        self.assertEqual([mgr.__class__ for _, mgr, _ in managers],
                         [models.Manager, FoodManager, FoodManager])
        self.assertIs(managers[0][1], Food._default_manager)
 def test_create_model(self):
     operation = migrations.CreateModel("Pony", [
         ('pony_id', models.AutoField(primary_key=True)),
         ('pink', models.IntegerField(default=1)),
     ])
     project_state = ProjectState()
     new_state = project_state.clone()
     operation.state_forwards('tests', new_state)
     self.assertTableNotExists('tests_pony')
     with connection.schema_editor() as editor:
         operation.database_forwards('tests', editor, project_state, new_state)
     self.assertTableExists('tests_pony')
     with connection.schema_editor() as editor:
         operation.database_backwards('tests', editor, new_state, project_state)
     self.assertTableNotExists('tests_pony')
Exemple #11
0
    def test_self_relation(self):
        """
        #24513 - Modifying an object pointing to itself would cause it to be
        rendered twice and thus breaking its related M2M through objects.
        """
        class A(models.Model):
            to_a = models.ManyToManyField('something.A', symmetrical=False)

            class Meta:
                app_label = "something"

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

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

        operation = AlterField(
            model_name="a",
            name="to_a",
            field=models.ManyToManyField("something.A", symmetrical=False, blank=True)
        )
        # At this point the model would be rendered twice causing its related
        # M2M through objects to point to an old copy and thus breaking their
        # attribute lookup.
        operation.state_forwards("something", project_state)

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

        # Tests that the old model's _meta is still consistent
        field_to_a_old = model_a_old._meta.get_field("to_a")
        self.assertEqual(field_to_a_old.m2m_field_name(), "from_a")
        self.assertEqual(field_to_a_old.m2m_reverse_field_name(), "to_a")
        self.assertIs(field_to_a_old.related_model, model_a_old)
        self.assertIs(field_to_a_old.remote_field.through._meta.get_field('to_a').related_model, model_a_old)
        self.assertIs(field_to_a_old.remote_field.through._meta.get_field('from_a').related_model, model_a_old)

        # Tests that the new model's _meta is still consistent
        field_to_a_new = model_a_new._meta.get_field("to_a")
        self.assertEqual(field_to_a_new.m2m_field_name(), "from_a")
        self.assertEqual(field_to_a_new.m2m_reverse_field_name(), "to_a")
        self.assertIs(field_to_a_new.related_model, model_a_new)
        self.assertIs(field_to_a_new.remote_field.through._meta.get_field('to_a').related_model, model_a_new)
        self.assertIs(field_to_a_new.remote_field.through._meta.get_field('from_a').related_model, model_a_new)
    def test_total_deconstruct(self):
        loader = MigrationLoader(None, load=True, ignore_no_migrations=True)
        loader.disk_migrations = {t: v for t, v in loader.disk_migrations.items() if t[0] != 'testapp'}
        app_labels = {"testapp"}
        questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=True)
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )
        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name="my_fake_migration_for_test_deconstruct",
        )
        self.assertGreater(len(changes), 0)
        for app_label, app_migrations in changes.items():
            for migration in app_migrations:
                # Describe the migration
                writer = MigrationWriter(migration)

                migration_string = writer.as_string()
                self.assertNotEqual(migration_string, "")
    def handle(self, *args, **kwargs):

        changed = set()
        ignore_list = ['authtools']  # dependencies that we don't care about migrations for (usually for testing only)

        self.stdout.write("Checking...")
        for db in settings.DATABASES.keys():

            try:
                executor = MigrationExecutor(connections[db])
            except OperationalError:
                sys.exit("Unable to check migrations: cannot connect to database\n")

            autodetector = MigrationAutodetector(
                executor.loader.project_state(),
                ProjectState.from_apps(apps),
            )

            changed.update(autodetector.changes(graph=executor.loader.graph).keys())

        for ignore in ignore_list:
            if ignore in changed:
                changed.remove(ignore)

        if changed:
            sys.exit("Apps with model changes but no corresponding migration file: %(changed)s\n" % {
                'changed': list(changed)
            })
        else:
            sys.stdout.write("All migration files present\n")
    def handle(self, *args, **kwargs):

        changed = set()

        self.stdout.write("Checking...")
        for db in settings.DATABASES.keys():

            try:
                executor = MigrationExecutor(connections[db])
            except OperationalError:
                self.stdout.write("Unable to check migrations: cannot connect to database '{}'.\n".format(db))
                sys.exit(1)

            all_apps = apps.app_configs.keys()
            questioner = InteractiveMigrationQuestioner(specified_apps=all_apps, dry_run=True)

            autodetector = MigrationAutodetector(
                executor.loader.project_state(),
                ProjectState.from_apps(apps),
                questioner,
            )

            changed.update(autodetector.changes(graph=executor.loader.graph, convert_apps=all_apps).keys())

        if changed:
            self.stdout.write(
                "Apps with model changes but no corresponding migration file: {!r}\n".format(
                    list(changed)
                )
            )
            sys.exit(1)
        else:
            self.stdout.write("All migration files present.\n")
            sys.exit(0)
Exemple #15
0
    def test_no_duplicate_managers(self):
        """
        When a manager is added with `use_in_migrations = True` and a parent
        model had a manager with the same name and `use_in_migrations = True`,
        the parent's manager shouldn't appear in the model state (#26881).
        """
        new_apps = Apps(['migrations'])

        class PersonManager(models.Manager):
            use_in_migrations = True

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

            class Meta:
                abstract = True

        class BossManager(PersonManager):
            use_in_migrations = True

        class Boss(Person):
            objects = BossManager()

            class Meta:
                app_label = 'migrations'
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        boss_state = project_state.models['migrations', 'boss']
        self.assertEqual(boss_state.managers, [('objects', Boss.objects)])
    def handle(self, *args, **options):
        changed = set()

        self.stdout.write("Checking...")
        for db in settings.DATABASES.keys():
            try:
                executor = MigrationExecutor(connections[db])
            except OperationalError:
                sys.exit("Unable to check migrations: cannot connect to database\n")

            autodetector = MigrationAutodetector(
                executor.loader.project_state(),
                ProjectState.from_apps(apps),
            )
            changed.update(autodetector.changes(graph=executor.loader.graph).keys())

        changed -= set(options['ignore'])

        if changed:
            sys.exit(
                "Apps with model changes but no corresponding migration file: %(changed)s\n" % {
                    'changed': list(changed)
                })
        else:
            sys.stdout.write("All migration files present\n")
Exemple #17
0
    def test_custom_base_manager(self):
        new_apps = Apps(['migrations'])

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

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

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

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

        project_state = ProjectState.from_apps(new_apps)

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

        author2_state = project_state.models['migrations', 'author2']
        self.assertEqual(author2_state.options['base_manager_name'], 'manager1')
        self.assertEqual(author2_state.managers, [
            ('manager1', Author2.manager1),
        ])
Exemple #18
0
    def test_mysql_cache_migration(self):
        out = StringIO()
        call_command('mysql_cache_migration', stdout=out)
        output = out.getvalue()

        # Lint it
        with captured_stdout() as stderr:
            errors = check_code(output)
        self.assertEqual(
            errors,
            0,
            "Encountered {} errors whilst trying to lint the mysql cache "
            "migration.\nMigration:\n\n{}\n\nLint errors:\n\n{}"
            .format(errors, output, stderr.getvalue())
        )

        # Dynamic import and check
        migration_mod = imp.new_module('0001_add_cache_tables')
        six.exec_(output, migration_mod.__dict__)
        self.assertTrue(hasattr(migration_mod, 'Migration'))
        migration = migration_mod.Migration
        self.assertTrue(hasattr(migration, 'dependencies'))
        self.assertTrue(hasattr(migration, 'operations'))

        # Since they all have the same table name, there should only be one
        # operation
        self.assertEqual(len(migration.operations), 1)

        # Now run the migration forwards and backwards to check it works
        operation = migration.operations[0]
        self.drop_table()
        self.assertTableNotExists(self.table_name)

        state = ProjectState()
        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_forwards("django_mysql_tests", editor,
                                        state, new_state)
        self.assertTableExists(self.table_name)

        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_backwards("django_mysql_tests", editor,
                                         new_state, state)
        self.assertTableNotExists(self.table_name)

        self.create_table()
Exemple #19
0
    def test_create(self):
        """
        Tests making a ProjectState from an AppCache
        """

        new_app_cache = BaseAppCache()

        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"
                app_cache = new_app_cache
                unique_together = ["name", "bio"]

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

        class Book(models.Model):
            title = models.CharField(max_length=1000)
            author = models.ForeignKey(Author)
            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache
                verbose_name = "tome"
                db_table = "test_tome"

        project_state = ProjectState.from_app_cache(new_app_cache)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        book_state = project_state.models['migrations', 'book']
        
        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, {"unique_together": set(("name", "bio"))})
        self.assertEqual(author_state.bases, (models.Model, ))
        
        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        self.assertEqual([x for x, y in book_state.fields], ["id", "title", "author"])
        self.assertEqual(book_state.fields[1][1].max_length, 1000)
        self.assertEqual(book_state.fields[2][1].null, False)
        self.assertEqual(book_state.options, {"verbose_name": "tome", "db_table": "test_tome"})
        self.assertEqual(book_state.bases, (models.Model, ))
        
        self.assertEqual(author_proxy_state.app_label, "migrations")
        self.assertEqual(author_proxy_state.name, "AuthorProxy")
        self.assertEqual(author_proxy_state.fields, [])
        self.assertEqual(author_proxy_state.options, {"proxy": True, "ordering": ["name"]})
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))
    def write_migration(self, migration):
        loader = MigrationLoader(None, ignore_no_migrations=True)
        autodetector = MigrationAutodetector(loader.project_state(), ProjectState.from_apps(apps),)
        changes = autodetector.arrange_for_graph(changes={'share': [migration]}, graph=loader.graph,)

        for m in changes['share']:
            writer = MigrationWriter(m)
            with open(writer.path, 'wb') as fp:
                fp.write(writer.as_string())
Exemple #21
0
    def test_choices_iterator(self):
        """
        #24483 - ProjectState.from_apps should not destructively consume
        Field.choices iterators.
        """
        new_apps = Apps(["migrations"])
        choices = [('a', 'A'), ('b', 'B')]

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

        ProjectState.from_apps(new_apps)
        choices_field = Author._meta.get_field('choice')
        self.assertEqual(list(choices_field.choices), choices)
Exemple #22
0
    def test_missing_migrations(self):
        executor = MigrationExecutor(connection)
        autodetector = MigrationAutodetector(
            executor.loader.project_state(),
            ProjectState.from_apps(apps),
        )

        changes = autodetector.changes(graph=executor.loader.graph)

        self.assertEqual({}, changes)
Exemple #23
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)
 def test_create_model(self):
     """
     Tests that CreateModel honours multi-db settings.
     """
     operation = migrations.CreateModel(
         "Pony", [("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1))]
     )
     # Test the state alteration
     project_state = ProjectState()
     new_state = project_state.clone()
     operation.state_forwards("test_crmo", new_state)
     # Test the database alteration
     self.assertTableNotExists("test_crmo_pony")
     with connection.schema_editor() as editor:
         operation.database_forwards("test_crmo", editor, project_state, new_state)
     self.assertTableNotExists("test_crmo_pony")
     # And test reversal
     with connection.schema_editor() as editor:
         operation.database_backwards("test_crmo", editor, new_state, project_state)
     self.assertTableNotExists("test_crmo_pony")
Exemple #25
0
    def test_render(self):
        """
        Tests rendering a ProjectState into an AppCache.
        """
        project_state = ProjectState()
        project_state.add_model_state(ModelState(
            "migrations",
            "Tag",
            [
                ("id", models.AutoField(primary_key=True)),
                ("name", models.CharField(max_length=100)),
                ("hidden", models.BooleanField()),
            ],
            {},
            None,
        ))

        new_app_cache = project_state.render()
        self.assertEqual(new_app_cache.get_model("migrations", "Tag")._meta.get_field_by_name("name")[0].max_length, 100)
        self.assertEqual(new_app_cache.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False)
    def test_install_soname(self):
        """
        Test we can load the 'metadata_lock_info' library.
        """
        assert not plugin_exists("metadata_lock_info")

        state = ProjectState()
        operation = InstallSOName("metadata_lock_info.so")
        assert operation.describe() == "Installs library metadata_lock_info.so"

        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_forwards("testapp", editor,
                                        state, new_state)
        assert plugin_exists("metadata_lock_info")

        new_state = state.clone()
        with connection.schema_editor() as editor:
            operation.database_backwards("testapp", editor,
                                         new_state, state)
        assert not plugin_exists("metadata_lock_info")
 def test_for_missing_migrations(self):
     """Checks if there're models changes which aren't reflected in migrations."""
     migrations_loader = MigrationExecutor(connection).loader
     migrations_detector = MigrationAutodetector(
         from_state=migrations_loader.project_state(),
         to_state=ProjectState.from_apps(apps)
     )
     if migrations_detector.changes(graph=migrations_loader.graph):
         self.fail(
             'Your models have changes that are not yet reflected '
             'in a migration. You should add them now.'
         )
Exemple #28
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)
    def test_migrations_apply_to_templates(self):
        template = SchemaTemplate.objects.create(name='a')
        operation = migrations.CreateModel("Pony", [
            ('pony_id', models.AutoField(primary_key=True)),
            ('pink', models.IntegerField(default=1)),
        ])
        project_state = ProjectState()
        new_state = project_state.clone()
        operation.state_forwards('tests', new_state)

        template.activate()
        self.assertFalse('tests_pony' in get_table_list())

        with connection.schema_editor() as editor:
            operation.database_forwards('tests', editor, project_state, new_state)
        template.activate()
        self.assertTrue('tests_pony' in get_table_list())

        with connection.schema_editor() as editor:
            operation.database_backwards('tests', editor, new_state, project_state)
        template.activate()
        self.assertFalse('tests_pony' in get_table_list())
Exemple #30
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)
Exemple #31
0
    def handle(self, *app_labels, **options):

        self.verbosity = int(options.get('verbosity'))
        self.interactive = options.get('interactive')

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                cache.get_app(app_label)
            except ImproperlyConfigured:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    "App '%s' could not be found. Is it in INSTALLED_APPS?" %
                    app_label)
            sys.exit(2)

        # Load the current graph state. Takes a connection, but it's not used
        # (makemigrations doesn't look at the database state).
        loader = MigrationLoader(connections[DEFAULT_DB_ALIAS])

        # Detect changes
        autodetector = MigrationAutodetector(
            loader.graph.project_state(),
            ProjectState.from_app_cache(cache),
            InteractiveMigrationQuestioner(specified_apps=app_labels),
        )
        changes = autodetector.changes(graph=loader.graph,
                                       trim_to_apps=app_labels or None)

        # No changes? Tell them.
        if not changes:
            if len(app_labels) == 1:
                self.stdout.write("No changes detected in app '%s'" %
                                  app_labels.pop())
            elif len(app_labels) > 1:
                self.stdout.write("No changes detected in apps '%s'" %
                                  ("', '".join(app_labels)))
            else:
                self.stdout.write("No changes detected")
            return

        directory_created = {}
        for app_label, migrations in changes.items():
            self.stdout.write(
                self.style.MIGRATE_HEADING("Migrations for '%s':" %
                                           app_label) + "\n")
            for migration in migrations:
                # Describe the migration
                writer = MigrationWriter(migration)
                self.stdout.write(
                    "  %s:\n" % (self.style.MIGRATE_LABEL(writer.filename), ))
                for operation in migration.operations:
                    self.stdout.write("    - %s\n" % operation.describe())
                # Write it
                migrations_directory = os.path.dirname(writer.path)
                if not directory_created.get(app_label, False):
                    if not os.path.isdir(migrations_directory):
                        os.mkdir(migrations_directory)
                    init_path = os.path.join(migrations_directory,
                                             "__init__.py")
                    if not os.path.isfile(init_path):
                        open(init_path, "w").close()
                    # We just do this once per app
                    directory_created[app_label] = True
                with open(writer.path, "w") as fh:
                    fh.write(writer.as_string())
Exemple #32
0
    def handle(self, *args, **options):
        # self 是「命令处理对象」
        print('【django.core.management.commands.migrate.Command.handle】args:',
              args)
        #print('【django.core.management.commands.migrate.Command.handle】options:' )
        #for k, v in options.items():
        #    print(f'\t{k:<22}{v}')
        database = options['database']  # 默认值是 'default'
        if not options['skip_checks']:
            # 此方法定义在父类 django.core.management.base.BaseCommand 中
            self.check(databases=[database])

        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)

        # connections 是 django.db.utils.ConnectionHandler 类的实例,叫做「数据库连接处理对象」
        # 此处调用其 __getitem__ 方法,返回的是「数据库包装对象」
        # 以 MySQL 为例,其所属类定义在 django.db.backends.mysql.base 模块中
        # 所以下面的 connection 是 django.db.backends.mysql.base.DatabaseWrapper 类的实例
        connection = connections[database]
        print(
            '【django.core.management.commands.migrate.Command.handle】connection:',
            connection)

        # 此方法定义在实例的父类中,是空函数
        connection.prepare_database()

        # 下面这十几行代码用于处理数据版本控制相关的事情

        # 此 MigrationExecutor 类定义在 django.db.migrations.executor 模块中,其实例是「数据库版本迁移执行器」
        # 实例化时提供两个参数,它们分别会被赋值给实例的同名属性:
        # connection 是「数据库包装对象」,这是位置参数
        # process_callback 是可选参数,这里提供的是定义在当前类中的方法
        executor = MigrationExecutor(connection,
                                     self.migration_progress_callback)

        # executor 是「数据库版本迁移执行器」
        # executor.loader 是 django.db.migrations.loader.MigrationLoader 类的实例
        # 该实例的 connection 属性值就是 self
        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 app '%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 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:
            # executor 是 django.db.migrations.executor.MigrationExecutor 类的实例
            # 叫做「数据库版本迁移执行器」
            # executor.loader 是 django.db.migrations.loader.MigrationLoader 类的实例
            # 此实例的 graph 属性值是 django.db.migrations.graph.MigrationGraph 类的实例
            # 调用其 leaf_nodes 获得各个应用中最新版本的迁移文件名
            # targets 是列表,里面是二元元组,类似这样:
            # [('admin', '0003_logentry_add_action_flag_choices'), ...]
            targets = executor.loader.graph.leaf_nodes()
            #print('【django.core.management.commands.migrate.Command.handle】targets:')
            #for i in targets:
            #    print('\t', i)

        # executor 是 django.db.migrations.executor.MigrationExecutor 类的实例
        # 叫做「数据库版本迁移执行器」
        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 app: %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 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']
        # executor 是 django.db.migrations.executor.MigrationExecutor 类的实例,叫做「数据库版本迁移执行器」
        # 参数:
        # targets 列表,里面是类似 ('auth', '0012_alter_user_first_name_max_length') 这样的元组
        # plan 列表,里面是类似 (<Migration auth.0001_initial>, False) 这样的元组
        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,
        )
Exemple #33
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
Exemple #34
0
    def test_create(self):
        """
        Tests making a ProjectState from an Apps
        """

        new_apps = Apps(["migrations"])

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

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

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

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

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

            class Meta:
                app_label = "migrations"
                apps = new_apps
                verbose_name = "tome"
                db_table = "test_tome"

        class Food(models.Model):

            food_mgr = FoodManager('a', 'b')
            food_qs = FoodQuerySet.as_manager()
            food_no_mgr = NoMigrationFoodManager('x', 'y')

            class Meta:
                app_label = "migrations"
                apps = new_apps

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

        class FoodNoDefaultManager(models.Model):

            food_no_mgr = NoMigrationFoodManager('x', 'y')
            food_mgr = FoodManager('a', 'b')
            food_qs = FoodQuerySet.as_manager()

            class Meta:
                app_label = "migrations"
                apps = new_apps

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

        class FoodOrderedManagers(models.Model):
            # The managers on this model should be ordered by their creation
            # counter and not by the order in model body

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        sub_author_state = project_state.models['migrations', 'subauthor']
        book_state = project_state.models['migrations', 'book']
        food_state = project_state.models['migrations', 'food']
        food_no_managers_state = project_state.models['migrations',
                                                      'foodnomanagers']
        food_no_default_manager_state = project_state.models[
            'migrations', 'foodnodefaultmanager']
        food_order_manager_state = project_state.models['migrations',
                                                        'foodorderedmanagers']

        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, {
                "unique_together": {("name", "bio")},
                "index_together": {("bio", "age")}
            })
        self.assertEqual(author_state.bases, (models.Model, ))

        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        self.assertEqual([x for x, y in book_state.fields],
                         ["id", "title", "author", "contributors"])
        self.assertEqual(book_state.fields[1][1].max_length, 1000)
        self.assertEqual(book_state.fields[2][1].null, False)
        self.assertEqual(book_state.fields[3][1].__class__.__name__,
                         "ManyToManyField")
        self.assertEqual(book_state.options, {
            "verbose_name": "tome",
            "db_table": "test_tome"
        })
        self.assertEqual(book_state.bases, (models.Model, ))

        self.assertEqual(author_proxy_state.app_label, "migrations")
        self.assertEqual(author_proxy_state.name, "AuthorProxy")
        self.assertEqual(author_proxy_state.fields, [])
        self.assertEqual(author_proxy_state.options, {
            "proxy": True,
            "ordering": ["name"]
        })
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))

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

        # 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))

        # No explicit managers defined. Migrations will fall back to the default
        self.assertEqual(food_no_managers_state.managers, [])

        # food_mgr is used in migration but isn't the default mgr, hence add the
        # default
        self.assertEqual(
            [name for name, mgr in food_no_default_manager_state.managers],
            ['food_no_mgr', 'food_mgr'])
        self.assertEqual(
            food_no_default_manager_state.managers[0][1].__class__,
            models.Manager)
        self.assertIsInstance(food_no_default_manager_state.managers[1][1],
                              FoodManager)

        self.assertEqual(
            [name for name, mgr in food_order_manager_state.managers],
            ['food_mgr1', 'food_mgr2'])
        self.assertEqual(
            [mgr.args for name, mgr in food_order_manager_state.managers],
            [('a', 'b', 1, 2), ('x', 'y', 3, 4)])
Exemple #35
0
 def set_up_test_model(
     self,
     app_label,
     second_model=False,
     third_model=False,
     index=False,
     multicol_index=False,
     related_model=False,
     mti_model=False,
     proxy_model=False,
     manager_model=False,
     unique_together=False,
     options=False,
     db_table=None,
     index_together=False,
     constraints=None,
 ):
     """Creates a test model state and database table."""
     # Make the "current" state.
     model_options = {
         'swappable': 'TEST_SWAP_MODEL',
         'index_together': [['weight', 'pink']] if index_together else [],
         'unique_together': [['pink', 'weight']] if unique_together else [],
     }
     if options:
         model_options['permissions'] = [('can_groom', 'Can groom')]
     if db_table:
         model_options['db_table'] = db_table
     operations = [
         migrations.CreateModel(
             'Pony',
             [
                 ('id', models.AutoField(primary_key=True)),
                 ('pink', models.IntegerField(default=3)),
                 ('weight', models.FloatField()),
             ],
             options=model_options,
         )
     ]
     if index:
         operations.append(
             migrations.AddIndex(
                 'Pony',
                 models.Index(fields=['pink'], name='pony_pink_idx'),
             ))
     if multicol_index:
         operations.append(
             migrations.AddIndex(
                 'Pony',
                 models.Index(fields=['pink', 'weight'],
                              name='pony_test_idx'),
             ))
     if constraints:
         for constraint in constraints:
             operations.append(migrations.AddConstraint('Pony', constraint))
     if second_model:
         operations.append(
             migrations.CreateModel('Stable', [
                 ('id', models.AutoField(primary_key=True)),
             ]))
     if third_model:
         operations.append(
             migrations.CreateModel('Van', [
                 ('id', models.AutoField(primary_key=True)),
             ]))
     if related_model:
         operations.append(
             migrations.CreateModel(
                 'Rider',
                 [('id', models.AutoField(primary_key=True)),
                  ('pony', models.ForeignKey('Pony', models.CASCADE)),
                  ('friend', models.ForeignKey('self', models.CASCADE))],
             ))
     if mti_model:
         operations.append(
             migrations.CreateModel(
                 'ShetlandPony',
                 fields=[
                     ('pony_ptr',
                      models.OneToOneField(
                          'Pony',
                          models.CASCADE,
                          auto_created=True,
                          parent_link=True,
                          primary_key=True,
                          to_field='id',
                          serialize=False,
                      )),
                     ('cuteness', models.IntegerField(default=1)),
                 ],
                 bases=['%s.Pony' % app_label],
             ))
     if proxy_model:
         operations.append(
             migrations.CreateModel(
                 'ProxyPony',
                 fields=[],
                 options={'proxy': True},
                 bases=['%s.Pony' % app_label],
             ))
     if manager_model:
         from .models import FoodManager, FoodQuerySet
         operations.append(
             migrations.CreateModel(
                 'Food',
                 fields=[
                     ('id', models.AutoField(primary_key=True)),
                 ],
                 managers=[
                     ('food_qs', FoodQuerySet.as_manager()),
                     ('food_mgr', FoodManager('a', 'b')),
                     ('food_mgr_kwargs', FoodManager('x', 'y', 3, 4)),
                 ]))
     return self.apply_operations(app_label, ProjectState(), operations)
Exemple #36
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()
Exemple #37
0
    def handle(self, *args, **options):
        # We're monkey patching the __exit__ method of DatabaseSchemaEditor because it reenables constraint checking
        # which we explicitly DO NOT want to do. The problem is that without patching this if multiple tables have
        # incorrect foreign key constraints you can't fix one at a time - you have to fix both simultaneously (which
        # Django doesn't support). Fun times.
        DatabaseSchemaEditor.__exit__ = BaseDatabaseSchemaEditor.__exit__


        # I would MUCH prefer to do this by calling super().handle(), but we have to edit the "connection" object within
        # migrate to make this work. Instead, below is the code as copied from django.core.management.commands.migrate.handle()
        # aside from the handful of lines between rows 55 and 60.


        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()

        #### The below five lines of code are the new additions in migrate_no_fk
        try:
            constraint_check = connection.disable_constraint_checking()
        except:
            connection.connection = connection.connect()
            constraint_check = connection.disable_constraint_checking()
        #### End additions!

        # 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 app '%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 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

        # 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 app: %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: ") + "%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:"))
            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 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,
        )
Exemple #38
0
 def make_project_state(self, model_states):
     "Shortcut to make ProjectStates from lists of predefined models"
     project_state = ProjectState()
     for model_state in model_states:
         project_state.add_model_state(model_state.clone())
     return project_state
Exemple #39
0
 def migrate(self, ops, state=None):
     class Migration(migrations.Migration):
         operations = ops
     migration = Migration('name', 'schema_test_app')
     with connection.schema_editor() as schema_editor:
         return migration.apply(state or ProjectState.from_apps(self.apps), schema_editor)
Exemple #40
0
    def handle(self, *args, **options):

        self.verbosity = options.get('verbosity')
        self.interactive = options.get('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.get('database')
        connection = connections[db]

        # If they asked for a migration listing, quit main execution flow and show it
        if options.get("list", False):
            warnings.warn(
                "The 'migrate --list' command is deprecated. Use 'showmigrations' instead.",
                RemovedInDjango110Warning,
                stacklevel=2)
            self.stdout.ending = None  # Remove when #21429 is fixed
            return call_command(
                'showmigrations',
                '--list',
                app_labels=[options['app_label']]
                if options['app_label'] else None,
                database=db,
                no_color=options.get('no_color'),
                settings=options.get('settings'),
                stdout=self.stdout,
                traceback=options.get('traceback'),
                verbosity=self.verbosity,
            )

        # 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)

        # 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 (%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'] and options['migration_name']:
            app_label, migration_name = options['app_label'], options[
                'migration_name']
            if app_label not in executor.loader.migrated_apps:
                raise CommandError("App '%s' does not have migrations." %
                                   app_label)
            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']:
            app_label = options['app_label']
            if app_label not in executor.loader.migrated_apps:
                raise CommandError("App '%s' does not have migrations." %
                                   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)
        run_syncdb = options.get(
            '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(executor.loader.unmigrated_apps)))
            if target_app_labels_only:
                self.stdout.write(
                    self.style.MIGRATE_LABEL("  Apply all migrations: ") +
                    (", ".join(set(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]))

        emit_pre_migrate_signal(self.verbosity, self.interactive,
                                connection.alias)

        # 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:
            executor.check_replacements()
            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."))
        else:
            fake = options.get("fake")
            fake_initial = options.get("fake_initial")
            executor.migrate(targets,
                             plan,
                             fake=fake,
                             fake_initial=fake_initial)

        # 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)
Exemple #41
0
    def test_create(self):
        """
        Tests making a ProjectState from an AppCache
        """

        new_app_cache = BaseAppCache()

        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"
                app_cache = new_app_cache
                unique_together = ["name", "bio"]

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

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

            class Meta:
                app_label = "migrations"
                app_cache = new_app_cache
                verbose_name = "tome"
                db_table = "test_tome"

        project_state = ProjectState.from_app_cache(new_app_cache)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        book_state = project_state.models['migrations', 'book']

        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,
                         {"unique_together": set(("name", "bio"))})
        self.assertEqual(author_state.bases, (models.Model, ))

        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        self.assertEqual([x for x, y in book_state.fields],
                         ["id", "title", "author"])
        self.assertEqual(book_state.fields[1][1].max_length, 1000)
        self.assertEqual(book_state.fields[2][1].null, False)
        self.assertEqual(book_state.options, {
            "verbose_name": "tome",
            "db_table": "test_tome"
        })
        self.assertEqual(book_state.bases, (models.Model, ))

        self.assertEqual(author_proxy_state.app_label, "migrations")
        self.assertEqual(author_proxy_state.name, "AuthorProxy")
        self.assertEqual(author_proxy_state.fields, [])
        self.assertEqual(author_proxy_state.options, {
            "proxy": True,
            "ordering": ["name"]
        })
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))
Exemple #42
0
    def handle(self, *app_labels, **options):

        self.verbosity = int(options.get('verbosity'))
        self.interactive = options.get('interactive')
        self.dry_run = options.get('dry_run', False)
        self.merge = options.get('merge', False)

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    "App '%s' could not be found. Is it in INSTALLED_APPS?" %
                    app_label)
            sys.exit(2)

        # Load the current graph state. Takes a connection, but it's not used
        # (makemigrations doesn't look at the database state).
        # Also make sure the graph is built without unmigrated apps shoehorned in.
        loader = MigrationLoader(connections[DEFAULT_DB_ALIAS])
        loader.build_graph(ignore_unmigrated=True)

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()
        if conflicts and not self.merge:
            name_str = "; ".join("%s in %s" % (", ".join(names), app)
                                 for app, names in conflicts.items())
            raise CommandError(
                "Conflicting migrations detected (%s).\nTo fix them run 'python manage.py makemigrations --merge'"
                % name_str)

        # If they want to merge and there's nothing to merge, then politely exit
        if self.merge and not conflicts:
            self.stdout.write("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        # Detect changes
        autodetector = MigrationAutodetector(
            loader.graph.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=app_labels),
        )
        changes = autodetector.changes(graph=loader.graph,
                                       trim_to_apps=app_labels or None)

        # No changes? Tell them.
        if not changes and self.verbosity >= 1:
            if len(app_labels) == 1:
                self.stdout.write("No changes detected in app '%s'" %
                                  app_labels.pop())
            elif len(app_labels) > 1:
                self.stdout.write("No changes detected in apps '%s'" %
                                  ("', '".join(app_labels)))
            else:
                self.stdout.write("No changes detected")
            return

        directory_created = {}
        for app_label, app_migrations in changes.items():
            if self.verbosity >= 1:
                self.stdout.write(
                    self.style.MIGRATE_HEADING("Migrations for '%s':" %
                                               app_label) + "\n")
            for migration in app_migrations:
                # Describe the migration
                writer = MigrationWriter(migration)
                if self.verbosity >= 1:
                    self.stdout.write(
                        "  %s:\n" %
                        (self.style.MIGRATE_LABEL(writer.filename), ))
                    for operation in migration.operations:
                        self.stdout.write("    - %s\n" % operation.describe())
                # Write it
                if not self.dry_run:
                    migrations_directory = os.path.dirname(writer.path)
                    if not directory_created.get(app_label, False):
                        if not os.path.isdir(migrations_directory):
                            os.mkdir(migrations_directory)
                        init_path = os.path.join(migrations_directory,
                                                 "__init__.py")
                        if not os.path.isfile(init_path):
                            open(init_path, "w").close()
                        # We just do this once per app
                        directory_created[app_label] = True
                    migration_string = writer.as_string()
                    with open(writer.path, "wb") as fh:
                        fh.write(migration_string)
Exemple #43
0
    def handle(self, *app_labels, **options):
        self.verbosity = options['verbosity']
        self.interactive = options['interactive']
        self.dry_run = options['dry_run']
        self.merge = options['merge']
        self.empty = options['empty']
        self.migration_name = options['name']
        check_changes = options['check_changes']

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    "App '%s' could not be found. Is it in INSTALLED_APPS?" %
                    app_label)
            sys.exit(2)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)

        db = options['database']
        connection = connections[db]
        try:
            loader.check_consistent_migration_file(connection)
        except InconsistentMigrationFileHistory as e:
            self.stderr.write(str(e))

        # Raise an error if any migrations are applied before their dependencies.
        consistency_check_labels = {
            config.label
            for config in apps.get_app_configs()
        }
        # Non-default databases are only checked if database routers used.
        aliases_to_check = connections if settings.DATABASE_ROUTERS else [
            DEFAULT_DB_ALIAS
        ]
        for alias in sorted(aliases_to_check):
            connection = connections[alias]
            if (connection.settings_dict['ENGINE'] !=
                    'django.db.backends.dummy' and any(
                        # At least one model must be migrated to the database.
                        router.allow_migrate(connection.alias,
                                             app_label,
                                             model_name=model._meta.object_name
                                             )
                        for app_label in consistency_check_labels for model in
                        apps.get_app_config(app_label).get_models())):
                loader.check_consistent_history(connection)

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()

        # If app_labels is specified, filter out conflicting migrations for unspecified apps
        if app_labels:
            conflicts = {
                app_label: conflict
                for app_label, conflict in conflicts.items()
                if app_label in app_labels
            }

        if conflicts and not self.merge:
            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 want to merge and there's nothing to merge, then politely exit
        if self.merge and not conflicts:
            self.stdout.write("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        if self.interactive:
            questioner = InteractiveMigrationQuestioner(
                specified_apps=app_labels, dry_run=self.dry_run)
        else:
            questioner = NonInteractiveMigrationQuestioner(
                specified_apps=app_labels, dry_run=self.dry_run)
        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )

        # If they want to make an empty migration, make one for each app
        if self.empty:
            if not app_labels:
                raise CommandError(
                    "You must supply at least one app label when using --empty."
                )
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = {app: [Migration("custom", app)] for app in app_labels}
            changes = autodetector.arrange_for_graph(
                changes=changes,
                graph=loader.graph,
                migration_name=self.migration_name,
            )
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name=self.migration_name,
        )

        if not changes:
            # No changes? Tell them.
            if self.verbosity >= 1:
                if len(app_labels) == 1:
                    self.stdout.write("No changes detected in app '%s'" %
                                      app_labels.pop())
                elif len(app_labels) > 1:
                    self.stdout.write("No changes detected in apps '%s'" %
                                      ("', '".join(app_labels)))
                else:
                    self.stdout.write("No changes detected")
        else:
            self.write_migration_files(changes)
            if check_changes:
                sys.exit(1)
Exemple #44
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'] and options['migration_name']:
            app_label, migration_name = options['app_label'], options[
                'migration_name']
            if app_label not in executor.loader.migrated_apps:
                raise CommandError("App '%s' does not have migrations." %
                                   app_label)
            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']:
            app_label = options['app_label']
            if app_label not in executor.loader.migrated_apps:
                raise CommandError("App '%s' does not have migrations." %
                                   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)
        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(set(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_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,
        )
Exemple #45
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()
Exemple #46
0
    def test_render(self):
        """
        Tests rendering a ProjectState into an Apps.
        """
        project_state = ProjectState()
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="Tag",
                fields=[
                    ("id", models.AutoField(primary_key=True)),
                    ("name", models.CharField(max_length=100)),
                    ("hidden", models.BooleanField()),
                ],
            ))
        project_state.add_model(
            ModelState(
                app_label="migrations",
                name="SubTag",
                fields=[
                    ('tag_ptr',
                     models.OneToOneField(
                         auto_created=True,
                         primary_key=True,
                         to_field='id',
                         serialize=False,
                         to='migrations.Tag',
                     )),
                    ("awesome", models.BooleanField()),
                ],
                bases=("migrations.Tag", ),
            ))

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

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

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

        Food = new_apps.get_model("migrations", "Food")
        managers = sorted(Food._meta.managers)
        self.assertEqual([mgr.name for _, mgr, _ in managers],
                         ['default', 'food_mgr1', 'food_mgr2'])
        self.assertEqual([mgr.__class__ for _, mgr, _ in managers],
                         [models.Manager, FoodManager, FoodManager])
        self.assertIs(managers[0][1], Food._default_manager)
Exemple #47
0
    def handle(self, *app_labels, **options):
        self.verbosity = options['verbosity']
        self.interactive = options['interactive']
        self.dry_run = options['dry_run']
        self.empty = options['empty']
        self.migration_name = options['name']

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    "App '%s' could not be found. Is it in INSTALLED_APPS?" %
                    app_label)
            sys.exit(2)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)

        # Raise an error if any migrations are applied before their dependencies.
        consistency_check_labels = {
            config.label
            for config in apps.get_app_configs()
        }
        # Non-default databases are only checked if database routers used.
        aliases_to_check = connections if settings.DATABASE_ROUTERS else [
            DEFAULT_DB_ALIAS
        ]
        for alias in sorted(aliases_to_check):
            connection = connections[alias]
            if connection.settings_dict[
                    'ENGINE'] != 'django.db.backends.dummy' and any(
                        # At least one model must be migrated to the database.
                        router.allow_migrate(connection.alias,
                                             app_label,
                                             model_name=model._meta.object_name
                                             )
                        for app_label in consistency_check_labels for model in
                        apps.get_app_config(app_label).get_models()):
                loader.check_consistent_history(connection)

        questioner = NonInteractiveMigrationQuestioner(
            specified_apps=app_labels, dry_run=self.dry_run)
        ps = ProjectState.from_apps(apps)
        autodetector = MigrationAutodetector(
            ps,
            questioner,
        )

        # If they want to make an empty migration, make one for each app
        if self.empty:
            if not app_labels:
                raise CommandError(
                    "You must supply at least one app label when using --empty."
                )
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = {app: [Migration("custom", app)] for app in app_labels}
            changes = autodetector.arrange_for_graph(
                changes=changes,
                graph=loader.graph,
                migration_name=self.migration_name,
            )
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name=self.migration_name,
        )

        self.write_migration_files(changes)
Exemple #48
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 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
        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 app '%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 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))
                target = (app_label, migration.name)
                # Partially applied squashed migrations are not included in the
                # graph, use the last replacement instead.
                if (target not in executor.loader.graph.nodes
                        and target in executor.loader.replacements):
                    incomplete_migration = executor.loader.replacements[target]
                    target = incomplete_migration.replaces[-1]
                targets = [target]
            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()

        if options['prune']:
            if not options['app_label']:
                raise CommandError(
                    'Migrations can be pruned only when an app is specified.')
            if self.verbosity > 0:
                self.stdout.write('Pruning migrations:',
                                  self.style.MIGRATE_HEADING)
            to_prune = set(executor.loader.applied_migrations) - set(
                executor.loader.disk_migrations)
            squashed_migrations_with_deleted_replaced_migrations = [
                migration_key for migration_key, migration_obj in
                executor.loader.replacements.items()
                if any(replaced in to_prune
                       for replaced in migration_obj.replaces)
            ]
            if squashed_migrations_with_deleted_replaced_migrations:
                self.stdout.write(
                    self.style.NOTICE(
                        "  Cannot use --prune because the following squashed "
                        "migrations have their 'replaces' attributes and may not "
                        "be recorded as applied:"))
                for migration in squashed_migrations_with_deleted_replaced_migrations:
                    app, name = migration
                    self.stdout.write(f'    {app}.{name}')
                self.stdout.write(
                    self.style.NOTICE(
                        "  Re-run 'manage.py migrate' if they are not marked as "
                        "applied, and remove 'replaces' attributes in their "
                        "Migration classes."))
            else:
                to_prune = sorted(migration for migration in to_prune
                                  if migration[0] == app_label)
                if to_prune:
                    for migration in to_prune:
                        app, name = migration
                        if self.verbosity > 0:
                            self.stdout.write(self.style.MIGRATE_LABEL(
                                f'  Pruning {app}.{name}'),
                                              ending='')
                        executor.recorder.record_unapplied(app, name)
                        if self.verbosity > 0:
                            self.stdout.write(self.style.SUCCESS(' OK'))
                elif self.verbosity > 0:
                    self.stdout.write('  No migrations to prune.')

        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)
        if options['prune']:
            return

        # 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 app: %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,
            stdout=self.stdout,
            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 app(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,
            stdout=self.stdout,
            apps=post_migrate_apps,
            plan=plan,
        )
Exemple #49
0
    def handle(self, *app_labels, **options):
        self.verbosity = options['verbosity']
        self.interactive = options['interactive']
        self.dry_run = options['dry_run']
        self.merge = options['merge']
        self.empty = options['empty']
        self.migration_name = options['name']
        self.exit_code = options['exit_code']
        check_changes = options['check_changes']

        if self.exit_code:
            warnings.warn(
                "The --exit option is deprecated in favor of the --check option.",
                RemovedInDjango20Warning)

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    "App '%s' could not be found. Is it in INSTALLED_APPS?" %
                    app_label)
            sys.exit(2)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()

        # If app_labels is specified, filter out conflicting migrations for unspecified apps
        if app_labels:
            conflicts = {
                app_label: conflict
                for app_label, conflict in iteritems(conflicts)
                if app_label in app_labels
            }

        if conflicts and not self.merge:
            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 want to merge and there's nothing to merge, then politely exit
        if self.merge and not conflicts:
            self.stdout.write("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        if self.interactive:
            questioner = InteractiveMigrationQuestioner(
                specified_apps=app_labels, dry_run=self.dry_run)
        else:
            questioner = NonInteractiveMigrationQuestioner(
                specified_apps=app_labels, dry_run=self.dry_run)
        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )

        # If they want to make an empty migration, make one for each app
        if self.empty:
            if not app_labels:
                raise CommandError(
                    "You must supply at least one app label when using --empty."
                )
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = {app: [Migration("custom", app)] for app in app_labels}
            changes = autodetector.arrange_for_graph(
                changes=changes,
                graph=loader.graph,
                migration_name=self.migration_name,
            )
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name=self.migration_name,
        )

        if not changes:
            # No changes? Tell them.
            if self.verbosity >= 1:
                if len(app_labels) == 1:
                    self.stdout.write("No changes detected in app '%s'" %
                                      app_labels.pop())
                elif len(app_labels) > 1:
                    self.stdout.write("No changes detected in apps '%s'" %
                                      ("', '".join(app_labels)))
                else:
                    self.stdout.write("No changes detected")

            if self.exit_code:
                sys.exit(1)
        else:
            self.write_migration_files(changes)
            if check_changes:
                sys.exit(1)
Exemple #50
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,
        )
Exemple #51
0
    def test_create(self):
        """
        Tests making a ProjectState from an Apps
        """

        new_apps = Apps(["migrations"])

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

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

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

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

            class Meta:
                app_label = "migrations"
                apps = new_apps

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

            class Meta:
                app_label = "migrations"
                apps = new_apps
                verbose_name = "tome"
                db_table = "test_tome"

        project_state = ProjectState.from_apps(new_apps)
        author_state = project_state.models['migrations', 'author']
        author_proxy_state = project_state.models['migrations', 'authorproxy']
        sub_author_state = project_state.models['migrations', 'subauthor']
        book_state = project_state.models['migrations', 'book']

        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, {"unique_together": set([("name", "bio")]), "index_together": set([("bio", "age")])})
        self.assertEqual(author_state.bases, (models.Model, ))

        self.assertEqual(book_state.app_label, "migrations")
        self.assertEqual(book_state.name, "Book")
        self.assertEqual([x for x, y in book_state.fields], ["id", "title", "author", "contributors"])
        self.assertEqual(book_state.fields[1][1].max_length, 1000)
        self.assertEqual(book_state.fields[2][1].null, False)
        self.assertEqual(book_state.fields[3][1].__class__.__name__, "ManyToManyField")
        self.assertEqual(book_state.options, {"verbose_name": "tome", "db_table": "test_tome"})
        self.assertEqual(book_state.bases, (models.Model, ))

        self.assertEqual(author_proxy_state.app_label, "migrations")
        self.assertEqual(author_proxy_state.name, "AuthorProxy")
        self.assertEqual(author_proxy_state.fields, [])
        self.assertEqual(author_proxy_state.options, {"proxy": True, "ordering": ["name"]})
        self.assertEqual(author_proxy_state.bases, ("migrations.author", ))

        self.assertEqual(sub_author_state.app_label, "migrations")
        self.assertEqual(sub_author_state.name, "SubAuthor")
        self.assertEqual(len(sub_author_state.fields), 2)
        self.assertEqual(sub_author_state.bases, ("migrations.author", ))
Exemple #52
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
    def set_up_test_model(self, app_label, second_model=False, third_model=False, related_model=False, mti_model=False):
        """
        Creates a test model state and database table.
        """
        # Delete the tables if they already exist
        with connection.cursor() as cursor:
            # Start with ManyToMany tables
            try:
                cursor.execute("DROP TABLE %s_pony_stables" % app_label)
            except DatabaseError:
                pass
            try:
                cursor.execute("DROP TABLE %s_pony_vans" % app_label)
            except DatabaseError:
                pass

            # Then standard model tables
            try:
                cursor.execute("DROP TABLE %s_pony" % app_label)
            except DatabaseError:
                pass
            try:
                cursor.execute("DROP TABLE %s_stable" % app_label)
            except DatabaseError:
                pass
            try:
                cursor.execute("DROP TABLE %s_van" % app_label)
            except DatabaseError:
                pass
        # Make the "current" state
        operations = [migrations.CreateModel(
            "Pony",
            [
                ("id", models.AutoField(primary_key=True)),
                ("pink", models.IntegerField(default=3)),
                ("weight", models.FloatField()),
            ],
        )]
        if second_model:
            operations.append(migrations.CreateModel(
                "Stable",
                [
                    ("id", models.AutoField(primary_key=True)),
                ]
            ))
        if third_model:
            operations.append(migrations.CreateModel(
                "Van",
                [
                    ("id", models.AutoField(primary_key=True)),
                ]
            ))
        if related_model:
            operations.append(migrations.CreateModel(
                "Rider",
                [
                    ("id", models.AutoField(primary_key=True)),
                    ("pony", models.ForeignKey("Pony")),
                ],
            ))
        if mti_model:
            operations.append(migrations.CreateModel(
                "ShetlandPony",
                fields=[
                    ('pony_ptr', models.OneToOneField(
                        auto_created=True,
                        primary_key=True,
                        to_field='id',
                        serialize=False,
                        to='Pony',
                    )),
                    ("cuteness", models.IntegerField(default=1)),
                ],
                bases=['%s.Pony' % app_label],
            ))

        return self.apply_operations(app_label, ProjectState(), operations)
Exemple #54
0
    def handle(self, *args, **options):

        self.verbosity = int(options.get('verbosity'))
        self.interactive = options.get('interactive')
        self.show_traceback = options.get('traceback')
        self.load_initial_data = options.get('load_initial_data')
        self.test_database = options.get('test_database', False)

        # 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.get('database')
        connection = connections[db]

        # If they asked for a migration listing, quit main execution flow and show it
        if options.get("list", False):
            return self.show_migration_list(connection, args)

        # Work out which apps have migrations and which do not
        executor = MigrationExecutor(connection,
                                     self.migration_progress_callback)

        # 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 (%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 = False
        target_app_labels_only = True
        if len(args) > 2:
            raise CommandError(
                "Too many command-line arguments (expecting 'app_label' or 'app_label migrationname')"
            )
        elif len(args) == 2:
            app_label, migration_name = args
            if app_label not in executor.loader.migrated_apps:
                raise CommandError(
                    "App '%s' does not have migrations (you cannot selectively sync unmigrated apps)"
                    % app_label)
            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."
                        % (app_label, migration_name))
                except KeyError:
                    raise CommandError(
                        "Cannot find a migration matching '%s' from app '%s'."
                        % (app_label, migration_name))
                targets = [(app_label, migration.name)]
            target_app_labels_only = False
        elif len(args) == 1:
            app_label = args[0]
            if app_label not in executor.loader.migrated_apps:
                raise CommandError(
                    "App '%s' does not have migrations (you cannot selectively sync unmigrated apps)"
                    % app_label)
            targets = [
                key for key in executor.loader.graph.leaf_nodes()
                if key[0] == app_label
            ]
        else:
            targets = executor.loader.graph.leaf_nodes()
            run_syncdb = True

        plan = executor.migration_plan(targets)

        # 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(executor.loader.unmigrated_apps) or "(none)"))
            if target_app_labels_only:
                self.stdout.write(
                    self.style.MIGRATE_LABEL("  Apply all migrations: ") +
                    (", ".join(set(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]))

        # Run the syncdb phase.
        # If you ever manage to get rid of this, I owe you many, many drinks.
        # Note that pre_migrate is called from inside here, as it needs
        # the list of models about to be installed.
        if run_syncdb:
            if self.verbosity >= 1:
                self.stdout.write(
                    self.style.MIGRATE_HEADING(
                        "Synchronizing apps without migrations:"))
            created_models = self.sync_apps(connection,
                                            executor.loader.unmigrated_apps)
        else:
            created_models = []

        # 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 needed.")
                # If there's changes that aren't in migrations yet, tell them how to fix it.
                autodetector = MigrationAutodetector(
                    executor.loader.graph.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."
                        ))
        else:
            executor.migrate(targets, plan, fake=options.get("fake", False))

        # Send the post_migrate signal, so individual apps can do whatever they need
        # to do at this point.
        emit_post_migrate_signal(created_models, self.verbosity,
                                 self.interactive, connection.alias)
Exemple #55
0
    def handle(self, *app_labels, **options):
        self.written_files = []
        self.verbosity = options["verbosity"]
        self.interactive = options["interactive"]
        self.dry_run = options["dry_run"]
        self.merge = options["merge"]
        self.empty = options["empty"]
        self.migration_name = options["name"]
        if self.migration_name and not self.migration_name.isidentifier():
            raise CommandError(
                "The migration name must be a valid Python identifier.")
        self.include_header = options["include_header"]
        check_changes = options["check_changes"]
        self.scriptable = options["scriptable"]
        # If logs and prompts are diverted to stderr, remove the ERROR style.
        if self.scriptable:
            self.stderr.style_func = None

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        has_bad_labels = False
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError as err:
                self.stderr.write(str(err))
                has_bad_labels = True
        if has_bad_labels:
            sys.exit(2)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)

        # Raise an error if any migrations are applied before their dependencies.
        consistency_check_labels = {
            config.label
            for config in apps.get_app_configs()
        }
        # Non-default databases are only checked if database routers used.
        aliases_to_check = (connections if settings.DATABASE_ROUTERS else
                            [DEFAULT_DB_ALIAS])
        for alias in sorted(aliases_to_check):
            connection = connections[alias]
            if connection.settings_dict[
                    "ENGINE"] != "django.db.backends.dummy" and any(
                        # At least one model must be migrated to the database.
                        router.allow_migrate(connection.alias,
                                             app_label,
                                             model_name=model._meta.object_name
                                             )
                        for app_label in consistency_check_labels for model in
                        apps.get_app_config(app_label).get_models()):
                try:
                    loader.check_consistent_history(connection)
                except OperationalError as error:
                    warnings.warn(
                        "Got an error checking a consistent migration history "
                        "performed for database connection '%s': %s" %
                        (alias, error),
                        RuntimeWarning,
                    )
        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()

        # If app_labels is specified, filter out conflicting migrations for
        # unspecified apps.
        if app_labels:
            conflicts = {
                app_label: conflict
                for app_label, conflict in conflicts.items()
                if app_label in app_labels
            }

        if conflicts and not self.merge:
            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 want to merge and there's nothing to merge, then politely exit
        if self.merge and not conflicts:
            self.log("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        if self.interactive:
            questioner = InteractiveMigrationQuestioner(
                specified_apps=app_labels,
                dry_run=self.dry_run,
                prompt_output=self.log_output,
            )
        else:
            questioner = NonInteractiveMigrationQuestioner(
                specified_apps=app_labels,
                dry_run=self.dry_run,
                verbosity=self.verbosity,
                log=self.log,
            )
        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )

        # If they want to make an empty migration, make one for each app
        if self.empty:
            if not app_labels:
                raise CommandError(
                    "You must supply at least one app label when using --empty."
                )
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = {app: [Migration("custom", app)] for app in app_labels}
            changes = autodetector.arrange_for_graph(
                changes=changes,
                graph=loader.graph,
                migration_name=self.migration_name,
            )
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name=self.migration_name,
        )

        if not changes:
            # No changes? Tell them.
            if self.verbosity >= 1:
                if app_labels:
                    if len(app_labels) == 1:
                        self.log("No changes detected in app '%s'" %
                                 app_labels.pop())
                    else:
                        self.log("No changes detected in apps '%s'" %
                                 ("', '".join(app_labels)))
                else:
                    self.log("No changes detected")
        else:
            self.write_migration_files(changes)
            if check_changes:
                sys.exit(1)
Exemple #56
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', 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)
Exemple #57
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"))
Exemple #58
0
    def set_up_test_model(self,
                          app_label,
                          second_model=False,
                          third_model=False,
                          related_model=False,
                          mti_model=False,
                          proxy_model=False,
                          unique_together=False,
                          options=False,
                          db_table=None,
                          index_together=False):
        """
        Creates a test model state and database table.
        """
        # Delete the tables if they already exist
        table_names = [
            # Start with ManyToMany tables
            '_pony_stables',
            '_pony_vans',
            # Then standard model tables
            '_pony',
            '_stable',
            '_van',
        ]
        tables = [(app_label + table_name) for table_name in table_names]
        with connection.cursor() as cursor:
            table_names = connection.introspection.table_names(cursor)
            connection.disable_constraint_checking()
            sql_delete_table = connection.schema_editor().sql_delete_table
            with transaction.atomic():
                for table in tables:
                    if table in table_names:
                        cursor.execute(
                            sql_delete_table % {
                                "table": connection.ops.quote_name(table),
                            })
            connection.enable_constraint_checking()

        # Make the "current" state
        model_options = {
            "swappable": "TEST_SWAP_MODEL",
            "index_together": [["weight", "pink"]] if index_together else [],
            "unique_together": [["pink", "weight"]] if unique_together else [],
        }
        if options:
            model_options["permissions"] = [("can_groom", "Can groom")]
        if db_table:
            model_options["db_table"] = db_table
        operations = [
            migrations.CreateModel(
                "Pony",
                [
                    ("id", models.AutoField(primary_key=True)),
                    ("pink", models.IntegerField(default=3)),
                    ("weight", models.FloatField()),
                ],
                options=model_options,
            )
        ]
        if second_model:
            operations.append(
                migrations.CreateModel("Stable", [
                    ("id", models.AutoField(primary_key=True)),
                ]))
        if third_model:
            operations.append(
                migrations.CreateModel("Van", [
                    ("id", models.AutoField(primary_key=True)),
                ]))
        if related_model:
            operations.append(
                migrations.CreateModel(
                    "Rider",
                    [("id", models.AutoField(primary_key=True)),
                     ("pony", models.ForeignKey("Pony")),
                     ("friend", models.ForeignKey("self"))],
                ))
        if mti_model:
            operations.append(
                migrations.CreateModel(
                    "ShetlandPony",
                    fields=[
                        ('pony_ptr',
                         models.OneToOneField(
                             auto_created=True,
                             primary_key=True,
                             to_field='id',
                             serialize=False,
                             to='Pony',
                         )),
                        ("cuteness", models.IntegerField(default=1)),
                    ],
                    bases=['%s.Pony' % app_label],
                ))
        if proxy_model:
            operations.append(
                migrations.CreateModel(
                    "ProxyPony",
                    fields=[],
                    options={"proxy": True},
                    bases=['%s.Pony' % app_label],
                ))

        return self.apply_operations(app_label, ProjectState(), operations)
Exemple #59
0
    def handle(self, *app_labels, **options):

        self.verbosity = options.get('verbosity')
        self.interactive = options.get('interactive')
        self.dry_run = options.get('dry_run', False)
        self.merge = options.get('merge', False)
        self.empty = options.get('empty', False)

        # Make sure the app they asked for exists
        app_labels = set(app_labels)
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    "App '%s' could not be found. Is it in INSTALLED_APPS?" %
                    app_label)
            sys.exit(2)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()
        if conflicts and not self.merge:
            name_str = "; ".join("%s in %s" % (", ".join(names), app)
                                 for app, names in conflicts.items())
            raise CommandError(
                "Conflicting migrations detected (%s).\nTo fix them run 'python manage.py makemigrations --merge'"
                % name_str)

        # If they want to merge and there's nothing to merge, then politely exit
        if self.merge and not conflicts:
            self.stdout.write("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=app_labels,
                                           dry_run=self.dry_run),
        )

        # If they want to make an empty migration, make one for each app
        if self.empty:
            if not app_labels:
                raise CommandError(
                    "You must supply at least one app label when using --empty."
                )
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = dict(
                (app, [Migration("custom", app)]) for app in app_labels)
            changes = autodetector.arrange_for_graph(changes, loader.graph)
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
        )

        # No changes? Tell them.
        if not changes and self.verbosity >= 1:
            if len(app_labels) == 1:
                self.stdout.write("No changes detected in app '%s'" %
                                  app_labels.pop())
            elif len(app_labels) > 1:
                self.stdout.write("No changes detected in apps '%s'" %
                                  ("', '".join(app_labels)))
            else:
                self.stdout.write("No changes detected")
            return

        self.write_migration_files(changes)
    def handle(self, *args, **options):
        self.model_name = options['model_name']
        self.source_app = options['source_app']
        self.dest_app = options['dest_app']

        # make sure the apps exist
        app_labels = set([self.source_app, self.dest_app])
        bad_app_labels = set()
        for app_label in app_labels:
            try:
                apps.get_app_config(app_label)
            except LookupError:
                bad_app_labels.add(app_label)
        if bad_app_labels:
            for app_label in bad_app_labels:
                self.stderr.write(
                    self.style.ERROR(
                        "App '{}' could not be found. Is it in INSTALLED_APPS?"
                        .format(app_label)))
            sys.exit(2)

        if len(app_labels) == 1:
            self.stderr.write(
                self.style.ERROR(
                    "Cannot move '{}' within the same app '{}'.".format(
                        self.model_name, self.dest_app)))
            sys.exit(2)

        # load the current graph
        loader = MigrationLoader(None, ignore_no_migrations=True)

        questioner = NonInteractiveMigrationQuestioner()

        self.from_state = loader.project_state()
        self.to_state = ProjectState.from_apps(apps)

        autodetector = MigrationAutodetector(
            self.from_state,
            self.to_state,
            questioner,
        )

        _migrations = []
        rename_table = self._get_rename_table_migration()
        _migrations.append(rename_table)
        create_model = self._get_create_model_migration([
            (rename_table.app_label, rename_table.name),
        ])
        _migrations.append(create_model)
        model_fk = self._get_model_fk_migrations([
            (create_model.app_label, create_model.name),
        ])
        delete_model_deps = [
            (rename_table.app_label, rename_table.name),
            (create_model.app_label, create_model.name),
        ]
        for fk_migration in model_fk:
            _migrations.append(fk_migration)
            delete_model_deps.append(
                (fk_migration.app_label, fk_migration.name), )
        delete_model = self._get_delete_model_migration(delete_model_deps)
        _migrations.append(delete_model)

        changes = {}
        for migration in _migrations:
            changes.setdefault(migration.app_label, []).append(migration)
        changes = autodetector.arrange_for_graph(
            changes=changes,
            graph=loader.graph,
        )
        self.write_migration_files(changes)

        self.stdout.write(self.style.SUCCESS("Done!"))