コード例 #1
0
def find_unsafe_migrations(connection):
    loader = MigrationLoader(connection)

    disk_migrations = set(loader.disk_migrations.keys())
    new_migrations = disk_migrations.difference(loader.applied_migrations)

    unsafe_migrations = []
    for app_name, migration_name in new_migrations:
        migration = loader.get_migration(app_name, migration_name)
        project_state = loader.project_state((app_name, migration_name),
                                             at_end=False)

        result = analyze_migration(connection, migration, project_state)
        if result:
            unsafe_migrations.append(result)

    unsafe_migrations = sorted(unsafe_migrations,
                               key=operator.attrgetter("app_name",
                                                       "migration_name"))

    conflicts = loader.detect_conflicts()

    for app, names in conflicts.items():
        unsafe_migrations.append(
            MigrationConflict(app_name=app, migration_names=names))

    return unsafe_migrations
コード例 #2
0
    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, "")
コード例 #3
0
    def test_load(self):
        """
        Makes sure the loader can load the migrations for the test apps,
        and then render them out to a new Apps.
        """
        # Load and test the plan
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(
                ("migrations", "0002_second")),
            [
                ("migrations", "0001_initial"),
                ("migrations", "0002_second"),
            ],
        )
        # Now render it out!
        project_state = migration_loader.project_state(
            ("migrations", "0002_second"))
        self.assertEqual(len(project_state.models), 2)

        author_state = project_state.models["migrations", "author"]
        self.assertEqual([x for x, y in author_state.fields],
                         ["id", "name", "slug", "age", "rating"])

        book_state = project_state.models["migrations", "book"]
        self.assertEqual([x for x, y in book_state.fields], ["id", "author"])

        # Ensure we've included unmigrated apps in there too
        self.assertIn("basic", project_state.real_apps)
コード例 #4
0
    def handle(self, app_label, **options):

        self.verbosity = options.get('verbosity')
        fixture_name = options.get('fixture_name')
        self.dry_run = False

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

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

        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=[app_label]),
        )

        migration = Migration("custom", app_label)
        migration.operations.append(LoadFixtureMigration(fixture_name))
        migration.dependencies += loader.graph.nodes.keys()

        changes = {app_label: [migration]}
        changes = autodetector.arrange_for_graph(
            changes=changes,
            graph=loader.graph,
        )
        self.write_migration_files(changes)
        return
コード例 #5
0
def make_migration(app_label="tests", from_state=None, to_state=None):
    """Generates migrations based on the specified app's state."""

    app_labels = [app_label]

    loader = MigrationLoader(None, ignore_no_migrations=True)
    loader.check_consistent_history(connection)

    questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels,
                                                   dry_run=False)

    autodetector = MigrationAutodetector(
        from_state or loader.project_state(),
        to_state or ProjectState.from_apps(apps),
        questioner,
    )

    changes = autodetector.changes(
        graph=loader.graph,
        trim_to_apps=app_labels or None,
        convert_apps=app_labels or None,
        migration_name="test",
    )

    changes_for_app = changes.get(app_label)
    if not changes_for_app or len(changes_for_app) == 0:
        return None

    return changes_for_app[0]
コード例 #6
0
def check_missing_migrations():
    """Check that user model and migration files are in sync"""
    from django.db.migrations.autodetector import MigrationAutodetector
    from django.db.migrations.loader import MigrationLoader
    from django.db.migrations.questioner import (
        NonInteractiveMigrationQuestioner as Questioner, )
    from django.db.migrations.state import ProjectState

    loader = MigrationLoader(None, ignore_no_migrations=True)
    conflicts = loader.detect_conflicts()
    if conflicts:
        raise Exception(
            "Migration conflicts detected. Please fix your migrations.")
    questioner = Questioner(dry_run=True, specified_apps=None)
    autodetector = MigrationAutodetector(
        loader.project_state(),
        ProjectState.from_apps(apps),
        questioner,
    )
    changes = autodetector.changes(
        graph=loader.graph,
        trim_to_apps=None,
        convert_apps=None,
        migration_name=None,
    )
    if changes:
        raise Exception(
            "Migration changes detected. "
            "Please update or add to the migration file as appropriate")
    print("Migration-checker detected no problems.")
コード例 #7
0
    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, "")
コード例 #8
0
    def test_models_match_migrations(self):
        """Make sure that no model changes exist.

        This logic is taken from django's makemigrations.py file.
        Here just detect if model changes exist that require
        a migration, and if so we fail.
        """
        app_labels = ['django_celery_results']
        loader = MigrationLoader(None, ignore_no_migrations=True)
        questioner = NonInteractiveMigrationQuestioner(
            specified_apps=app_labels, dry_run=False)
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels,
            convert_apps=app_labels,
            migration_name='fake_name',
        )
        self.assertTrue(
            not changes,
            msg='Model changes exist that do not have a migration')
コード例 #9
0
ファイル: test_loader.py プロジェクト: CrazyChief/django
    def test_load(self):
        """
        Makes sure the loader can load the migrations for the test apps,
        and then render them out to a new Apps.
        """
        # Load and test the plan
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "0002_second")),
            [
                ("migrations", "0001_initial"),
                ("migrations", "0002_second"),
            ],
        )
        # Now render it out!
        project_state = migration_loader.project_state(("migrations", "0002_second"))
        self.assertEqual(len(project_state.models), 2)

        author_state = project_state.models["migrations", "author"]
        self.assertEqual(
            [x for x, y in author_state.fields],
            ["id", "name", "slug", "age", "rating"]
        )

        book_state = project_state.models["migrations", "book"]
        self.assertEqual(
            [x for x, y in book_state.fields],
            ["id", "author"]
        )

        # Ensure we've included unmigrated apps in there too
        self.assertIn("basic", project_state.real_apps)
コード例 #10
0
    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())
コード例 #11
0
 def run_backward_migration(self):
     """Run/unapply migrations from `migrate_to` to `migrate_from`."""
     if not self._migration_run:
         raise RuntimeError("Forward migration not run")
     loader = MigrationLoader(connection)
     self._migrate(self.migrate_from)
     self._apps = loader.project_state([(self.app_name, self.migrate_from)
                                        ]).apps
     self._migration_run = False
コード例 #12
0
 def run_forward_migration(self):
     """Run/apply migrations from `migrate_from` to `migrate_to`."""
     if self._migration_run:
         raise RuntimeError("Forward migration already run")
     loader = MigrationLoader(connection)
     self._migrate(self.migrate_to)
     self._apps = loader.project_state([(self.app_name, self.migrate_to)
                                        ]).apps
     self._migration_run = True
コード例 #13
0
 def _get_apps_for_migration(self, migration_states):
     loader = MigrationLoader(connection)
     full_names = []
     for app_name, migration_name in migration_states:
         if migration_name != 'zero':
             migration_name = loader.get_migration_by_prefix(
                 app_name, migration_name).name
             full_names.append((app_name, migration_name))
     state = loader.project_state(full_names)
     return state.apps
コード例 #14
0
 def _get_apps_for_migration(self, migration_states):
     loader = MigrationLoader(connection)
     full_names = []
     for app_name, migration_name in migration_states:
         migration_name = loader.get_migration_by_prefix(app_name, migration_name).name
         full_names.append((app_name, migration_name))
     state = loader.project_state(full_names)
     if django.VERSION < (1, 8):
         state.render()
     return state.apps
コード例 #15
0
 def _get_apps_for_migration(self, migration_states):
     loader = MigrationLoader(connection)
     full_names = []
     for app_name, migration_name in migration_states:
         if migration_name != 'zero':
             migration_name = loader.get_migration_by_prefix(
                 app_name, migration_name).name
             full_names.append((app_name, migration_name))
     state = loader.project_state(full_names)
     return state.apps
コード例 #16
0
 def _get_apps_for_migration(self, migration_states):
     loader = MigrationLoader(connection)
     full_names = []
     for app_name, migration_name in migration_states:
         migration_name = loader.get_migration_by_prefix(
             app_name, migration_name).name
         full_names.append((app_name, migration_name))
     state = loader.project_state(full_names)
     if django.VERSION < (1, 8):
         state.render()
     return state.apps
コード例 #17
0
    def handle(self, *app_labels, **options):
        # get supported options.
        self.verbosity = options['verbosity']
        self.dry_run = options['dry_run']
        self.merge = options['merge']
        self.migration_name = options['name']
        self.include_header = options['include_header']
        check_changes = options['check_changes']

        # validation application labels
        app_labels = set(app_labels)
        self.validate_applications(app_labels)

        # we don't check conflicts as regular makemigrations command.
        # we don't check if any migrations are applied before their dependencies as regular makemigrations command.

        # load migrations using same loader as in regular command
        loader = MigrationLoader(None, ignore_no_migrations=True)

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

        # overwritten autodetector. They detect only view changes.
        autodetector = ViewMigrationAutoDetector(
            from_state,
            to_state,
        )

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

        # it's copy paste from make migration command
        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)
コード例 #18
0
    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())
コード例 #19
0
ファイル: test_migrations.py プロジェクト: akrawchyk/wagtail
    def test_django_17_migrations(self):

        from django.apps import apps
        from django.db.migrations.loader import MigrationLoader
        from django.db.migrations.autodetector import MigrationAutodetector
        from django.db.migrations.state import ProjectState
        from django.db.migrations.questioner import MigrationQuestioner
        app_labels = set(app.label for app in apps.get_app_configs()
                         if app.name.startswith('wagtail.'))
        for app_label in app_labels:
            apps.get_app_config(app_label.split('.')[-1])
        loader = MigrationLoader(None, ignore_no_migrations=True)

        conflicts = dict(
            (app_label, conflict)
            for app_label, conflict in iteritems(loader.detect_conflicts())
            if app_label in app_labels
        )

        if conflicts:
            name_str = "; ".join("%s in %s" % (", ".join(names), app)
                                 for app, names in conflicts.items())
            self.fail("Conflicting migrations detected (%s)." % name_str)

        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            MigrationQuestioner(specified_apps=app_labels, dry_run=True),
        )

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

        if changes:
            apps = ', '.join(apps.get_app_config(label).name
                             for label in changes.keys())
            migrations = '\n'.join((
                '  {migration}\n{changes}'.format(
                    migration=migration,
                    changes='\n'.join('    {0}'.format(operation.describe())
                                      for operation in migration.operations))
                for (_, migrations) in changes.items()
                for migration in migrations))

            self.fail('Model changes with no migrations detected:\n%s' % migrations)
コード例 #20
0
ファイル: test_loader.py プロジェクト: Morfyo/JAVASCRIPT
    def test_load_unmigrated_dependency(self):
        """
        Makes sure the loader can load migrations with a dependency on an unmigrated app.
        """
        # Load and test the plan
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "0001_initial")),
            [("contenttypes", "0001_initial"), ("auth", "0001_initial"), ("migrations", "0001_initial")],
        )
        # Now render it out!
        project_state = migration_loader.project_state(("migrations", "0001_initial"))
        self.assertEqual(len([m for a, m in project_state.models if a == "migrations"]), 1)

        book_state = project_state.models["migrations", "book"]
        self.assertEqual([x for x, y in book_state.fields], ["id", "user"])
コード例 #21
0
    def test_django_17_migrations(self):

        from django.apps import apps
        from django.db.migrations.loader import MigrationLoader
        from django.db.migrations.autodetector import MigrationAutodetector
        from django.db.migrations.state import ProjectState
        from django.db.migrations.questioner import MigrationQuestioner
        app_labels = set(app.label for app in apps.get_app_configs()
                         if app.name.startswith('wagtail.'))
        for app_label in app_labels:
            apps.get_app_config(app_label.split('.')[-1])
        loader = MigrationLoader(None, ignore_no_migrations=True)

        conflicts = dict(
            (app_label, conflict)
            for app_label, conflict in iteritems(loader.detect_conflicts())
            if app_label in app_labels)

        if conflicts:
            name_str = "; ".join("%s in %s" % (", ".join(names), app)
                                 for app, names in conflicts.items())
            self.fail("Conflicting migrations detected (%s)." % name_str)

        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            MigrationQuestioner(specified_apps=app_labels, dry_run=True),
        )

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

        if changes:
            apps = ', '.join(
                apps.get_app_config(label).name for label in changes.keys())
            migrations = '\n'.join(('  {migration}\n{changes}'.format(
                migration=migration,
                changes='\n'.join('    {0}'.format(operation.describe())
                                  for operation in migration.operations))
                                    for (_, migrations) in changes.items()
                                    for migration in migrations))

            self.fail('Model changes with no migrations detected:\n%s' %
                      migrations)
コード例 #22
0
    def __iter__(self):
        l = MigrationLoader(self.conn)
        for app, migrations in self.migrations.iteritems():
            yield "-- Application: " + app
            migrations.sort()
            for name in migrations:
                yield "-- Migration: " + name
                state = l.project_state((app, name), at_end=False)
                mig = l.graph.nodes[(app, name)]
                self.commented = False
                if not len(mig.operations):
                    yield "-- Blank migration"
                    yield """INSERT INTO "django_migrations" ("app", "name", "applied") VALUES ('{}', '{}', now());""".format(app, name)
                    yield ""
                    continue

                for op in mig.operations:
                    # reject mutating changes
                    if op.__class__ in DANGEROUS_OPS:
                        yield "-- DANGEROUS OPERATION FOUND. : {}".format(
                            op.__class__.__name__
                        )
                        self.commented = True
                        break
                try:
                    se = self.conn.schema_editor(collect_sql=True)
                    se.deferred_sql = []
                    # Hack!!! do not drop default on column creation
                    se.skip_default = lambda x: True
                    mig.apply(state, se, collect_sql=True)
                    lines = se.collected_sql + se.deferred_sql
                except:
                    yield "-- GOT AN EXCEPTION!"
                else:
                    if not lines:
                        yield "-- NO SQL MIGRATION HERE"
                        self.commented = True
                    else:
                        yield self.format("BEGIN;")
                    for line in lines:
                        yield self.format(line)
                        #yield ""
                    yield self.format("""INSERT INTO "django_migrations" ("app", "name", "applied") VALUES ('{}', '{}', now());""".format(app, name))
                    if lines:
                        yield self.format("COMMIT;")
                yield ""
コード例 #23
0
    def test_no_migration_left(self):
        loader = MigrationLoader(None, ignore_no_migrations=True)
        conflicts = loader.detect_conflicts()
        app_labels = ['cities_light']

        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=True),
        )

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

        assert 'cities_light' not in changes
コード例 #24
0
    def handle(self, app_label, *models, **options):
        self.verbosity = int(options.get('verbosity'))
        self.interactive = options.get('interactive')
        self.dry_run = options.get('dry_run', False)

        if not models and '.' in app_label:
            app_label, models = app_label.split('.', 1)
            models = [models]

        try:
            apps.get_app_config(app_label)
        except LookupError:
            self.stderr.write("App '%s' could not be found. Is it in INSTALLED_APPS?" % app_label)
            sys.exit(2)

        # We want to basically write an empty migration, but with some
        # extra bits.

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

        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=[app_label]),
        )

        changes = autodetector.arrange_for_graph({
            app_label: [Migration("audit_tables", app_label)]
        }, loader.graph)

        migration = changes[app_label][0]
        migration.dependencies.append(
            ('audit', '0001_initial')
        )
        migration.name = 'audit_%s' % ('_'.join(models[:3]))

        for model_name in models:
            model = apps.get_model(app_label, model_name)
            migration.operations.append(postgres.audit.operations.AuditModel(model))

        self.write_migration_files(changes)
コード例 #25
0
 def handle(self, app_label, *models, **options):
     self.verbosity = int(options.get('verbosity'))
     self.interactive = options.get('interactive')
     self.dry_run = options.get('dry_run', False)
     
     if not models and '.' in app_label:
         app_label, models = app_label.split('.', 1)
         models = [models]
     
     try:
         apps.get_app_config(app_label)
     except LookupError:
         self.stderr.write("App '%s' could not be found. Is it in INSTALLED_APPS?" % app_label)
         sys.exit(2)
     
     # We want to basically write an empty migration, but with some
     # extra bits.
     
     # 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)
     
     # Set up autodetector
     autodetector = MigrationAutodetector(
         loader.project_state(),
         ProjectState.from_apps(apps),
         InteractiveMigrationQuestioner(specified_apps=[app_label]),
     )
     
     changes = autodetector.arrange_for_graph({
         app_label: [Migration("audit_tables", app_label)]
     }, loader.graph)
     
     migration = changes[app_label][0]
     migration.dependencies.append(
         ('audit', '0001_initial')
     )
     migration.name = 'audit_%s' % ('_'.join(models[:3]))
     
     for model_name in models:
         model = apps.get_model(app_label, model_name)
         migration.operations.append(postgres.audit.operations.AuditModel(model))
     
     self.write_migration_files(changes)
コード例 #26
0
    def test__migrations(self):
        app_labels = {
            app.label
            for app in apps.get_app_configs()
            if app.name.startswith("wagtail.")
        }
        for app_label in app_labels:
            apps.get_app_config(app_label.split(".")[-1])
        loader = MigrationLoader(None, ignore_no_migrations=True)

        conflicts = {
            (app_label, conflict)
            for app_label, conflict in loader.detect_conflicts().items()
            if app_label in app_labels
        }

        if conflicts:
            name_str = "; ".join("%s in %s" % (", ".join(names), app)
                                 for app, names in conflicts.items())
            self.fail("Conflicting migrations detected (%s)." % name_str)

        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            MigrationQuestioner(specified_apps=app_labels, dry_run=True),
        )

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

        if changes:
            migrations = "\n".join(("  {migration}\n{changes}".format(
                migration=migration,
                changes="\n".join("    {0}".format(operation.describe())
                                  for operation in migration.operations),
            ) for (_, migrations) in changes.items()
                                    for migration in migrations))

            self.fail("Model changes with no migrations detected:\n%s" %
                      migrations)
コード例 #27
0
    def create_runpython_migration(self,
                                   app_label,
                                   forwards_backwards,
                                   extra_functions,
                                   extra_dependencies=None):

        # Copy source code, so that we can uninstall this helper app
        # and the migrations still work.
        extra_func_code = "\n\n".join(
            inspect.getsource(f) for f in extra_functions)

        loader = MigrationLoader(None, ignore_no_migrations=True)
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            None,
        )

        changes = {app_label: [Migration("custom", app_label)]}
        changes = autodetector.arrange_for_graph(
            changes=changes,
            graph=loader.graph,
        )

        for app_label, app_migrations in changes.items():
            for migration in app_migrations:
                if extra_dependencies is not None:
                    migration.dependencies.extend(extra_dependencies)
                writer = MigrationWriter(migration)

                migration_string = writer.as_string().decode('utf-8')

                # Add support functions:
                migration_string = migration_string.replace(
                    "\nclass Migration", forwards_backwards + "\n\n" +
                    extra_func_code + "\n\n" + "\nclass Migration")

                # Add operations:
                migration_string = migration_string.replace(
                    "operations = [", "operations = [\n"
                    "        migrations.RunPython(forwards, backwards),")
                with open(writer.path, "wb") as fh:
                    fh.write(migration_string.encode('utf-8'))
コード例 #28
0
    def test_load_unmigrated_dependency(self):
        """
        Makes sure the loader can load migrations with a dependency on an unmigrated app.
        """
        # Load and test the plan
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(("migrations", "0001_initial")),
            [
                ('contenttypes', '0001_initial'),
                ('auth', '0001_initial'),
                ("migrations", "0001_initial"),
            ],
        )
        # Now render it out!
        project_state = migration_loader.project_state(("migrations", "0001_initial"))
        self.assertEqual(len([m for a, m in project_state.models if a == "migrations"]), 1)

        book_state = project_state.models["migrations", "book"]
        self.assertEqual(list(book_state.fields), ['id', 'user'])
コード例 #29
0
 def __iter__(self):
     l = MigrationLoader(self.conn)
     for app, migrations in self.migrations.iteritems():
         yield "-- Application: " + app
         migrations.sort()
         for name in migrations:
             yield "-- Migration: " + name
             state = l.project_state((app, name), at_end=False)
             mig = l.graph.nodes[(app, name)]
             self.commented = False
             if not len(mig.operations):
                 yield "-- Blank migration"
                 yield """INSERT INTO "django_migrations" ("app", "name", "applied") VALUES ('{}', '{}', now());""".format(app, name)
                 yield ""
                 continue
             for op in mig.operations:
                 # reject mutating changes
                 if op.__class__ in DANGEROUS_OPS:
                     yield "-- DANGEROUS OPERATION FOUND. : {}".format(
                         op.__class__.__name__
                     )
                     self.commented = True
                     break
             try:
                 se = self.conn.schema_editor(collect_sql=True)
                 se.deferred_sql = []
                 # Hack!!! do not drop default on column creation
                 se.skip_default = lambda x: True
                 mig.apply(state, se, collect_sql=True)
                 lines = se.collected_sql + se.deferred_sql
             except:
                 yield "-- GOT AN EXCEPTION!"
             else:
                 if not lines:
                     yield "-- NO SQL MIGRATION HERE"
                     self.commented = True
                 for line in lines:
                     yield self.format(line)
                     yield ""
                 yield self.format("""INSERT INTO "django_migrations" ("app", "name", "applied") VALUES ('{}', '{}', now());""".format(app, name))
             yield ""
コード例 #30
0
    def test_load_unmigrated_dependency(self):
        """
        Makes sure the loader can load migrations with a dependency on an unmigrated app.
        """
        # Load and test the plan
        migration_loader = MigrationLoader(connection)
        self.assertEqual(
            migration_loader.graph.forwards_plan(
                ("migrations", "0001_initial")),
            [
                ("migrations", "0001_initial"),
            ],
        )
        # Now render it out!
        project_state = migration_loader.project_state(
            ("migrations", "0001_initial"))
        self.assertEqual(
            len([m for a, m in project_state.models if a == "migrations"]), 1)

        book_state = project_state.models["migrations", "book"]
        self.assertEqual([x for x, y in book_state.fields], ["id", "user"])
コード例 #31
0
def make_migration(app_label="tests"):
    """Generates migrations based on the specified app's state."""

    app_labels = [app_label]

    loader = MigrationLoader(None, ignore_no_migrations=True)
    loader.check_consistent_history(connection)

    questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels,
                                                   dry_run=False)

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

    changes = autodetector.changes(
        graph=loader.graph,
        trim_to_apps=app_labels or None,
        convert_apps=app_labels or None,
        migration_name="test",
    )

    return changes[app_label][0]
コード例 #32
0
    def handle(self, app_label, **options):

        self.verbosity = options.get('verbosity')
        fixture_name = options.get('fixture_name')
        self.dry_run = False

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

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

        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=[app_label]),
            )

        migration = Migration("custom", app_label)
        migration.operations.append(LoadFixtureMigration(fixture_name))
        migration.dependencies += loader.graph.nodes.keys()

        changes = {
            app_label: [migration]
        }
        changes = autodetector.arrange_for_graph(
            changes=changes,
            graph=loader.graph,
        )
        self.write_migration_files(changes)
        return
コード例 #33
0
def check_model_state():
    """Checks if the state of local models is represented by migration files.

    This code follows the logic of Djangos makemigrations
    https://github.com/django/django/blob/master/django/core/management/commands/makemigrations.py

    Raises:
        MigrationStateError: If the loader detects conflicts or unapplied changes.

    Future:
        It might be desirable to allow partial checks by, e.g., providing an app_labels
        argument.
    """

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

        # Identify conflicting apps
        conflicts = loader.detect_conflicts()

        # Set up autodetector and detect changes
        changes = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
        ).changes(graph=loader.graph)
    except Exception as error:
        raise MigrationStateError(
            f"Error when checking state of migrations conflicts:\n{error}")

    if conflicts:
        raise MigrationStateError("Conflicting migrations", conflicts)

    if changes:
        raise MigrationStateError(f"Migrations have changed", changes)
コード例 #34
0
ファイル: makemigrations.py プロジェクト: SKR-01/django
    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)

        # 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 app_labels:
                    if len(app_labels) == 1:
                        self.stdout.write("No changes detected in app '%s'" % app_labels.pop())
                    else:
                        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)
コード例 #35
0
ファイル: django.py プロジェクト: zvrr/sentry
class Django19MigrationExecutor(object):
    """
    End-to-end migration execution - loads migrations, and runs them
    up or down to a specified set of targets.

    Backported from Django 1.9.1, containing changes from:

    https://github.com/django/django/commit/5aa55038ca9ac44b440b56d1fc4e79c876e51393
    https://github.com/django/django/commit/a80fb8ae24e77abf20047b9dfe867b4b53a8d648
    """
    def __init__(self, connection, progress_callback=None):
        self.connection = connection
        self.loader = MigrationLoader(self.connection)
        self.recorder = MigrationRecorder(self.connection)
        self.progress_callback = progress_callback

    def migration_plan(self, targets, clean_start=False):
        """
        Given a set of targets, returns a list of (Migration instance, backwards?).
        """
        plan = []
        if clean_start:
            applied = set()
        else:
            applied = set(self.loader.applied_migrations)
        for target in targets:
            # If the target is (app_label, None), that means unmigrate everything
            if target[1] is None:
                for root in self.loader.graph.root_nodes():
                    if root[0] == target[0]:
                        for migration in self.loader.graph.backwards_plan(
                                root):
                            if migration in applied:
                                plan.append(
                                    (self.loader.graph.nodes[migration], True))
                                applied.remove(migration)
            # If the migration is already applied, do backwards mode,
            # otherwise do forwards mode.
            elif target in applied:
                # Don't migrate backwards all the way to the target node (that
                # may roll back dependencies in other apps that don't need to
                # be rolled back); instead roll back through target's immediate
                # child(ren) in the same app, and no further.
                next_in_app = sorted(
                    n for n in self.loader.graph.node_map[target].children
                    if n[0] == target[0])
                for node in next_in_app:
                    for migration in self.loader.graph.backwards_plan(node):
                        if migration in applied:
                            plan.append(
                                (self.loader.graph.nodes[migration], True))
                            applied.remove(migration)
            else:
                for migration in self.loader.graph.forwards_plan(target):
                    if migration not in applied:
                        plan.append(
                            (self.loader.graph.nodes[migration], False))
                        applied.add(migration)
        return plan

    def migrate(self, targets, plan=None, fake=False, fake_initial=False):
        """
        Migrates the database up to the given targets.

        Django first needs to create all project states before a migration is
        (un)applied and in a second step run all the database operations.
        """
        if plan is None:
            plan = self.migration_plan(targets)
        # Create the forwards plan Django would follow on an empty database
        full_plan = self.migration_plan(self.loader.graph.leaf_nodes(),
                                        clean_start=True)

        all_forwards = all(not backwards for mig, backwards in plan)
        all_backwards = all(backwards for mig, backwards in plan)

        if not plan:
            pass  # Nothing to do for an empty plan
        elif all_forwards == all_backwards:
            # This should only happen if there's a mixed plan
            raise InvalidMigrationPlan(
                "Migration plans with both forwards and backwards migrations "
                "are not supported. Please split your migration process into "
                "separate plans of only forwards OR backwards migrations.",
                plan,
            )
        elif all_forwards:
            self._migrate_all_forwards(plan,
                                       full_plan,
                                       fake=fake,
                                       fake_initial=fake_initial)
        else:
            # No need to check for `elif all_backwards` here, as that condition
            # would always evaluate to true.
            self._migrate_all_backwards(plan, full_plan, fake=fake)

        self.check_replacements()

    def _migrate_all_forwards(self, plan, full_plan, fake, fake_initial):
        """
        Take a list of 2-tuples of the form (migration instance, False) and
        apply them in the order they occur in the full_plan.
        """
        migrations_to_run = {m[0] for m in plan}
        state = ProjectState(real_apps=list(self.loader.unmigrated_apps))
        for migration, _ in full_plan:
            if not migrations_to_run:
                # We remove every migration that we applied from this set so
                # that we can bail out once the last migration has been applied
                # and don't always run until the very end of the migration
                # process.
                break
            if migration in migrations_to_run:
                if "apps" not in state.__dict__:
                    if self.progress_callback:
                        self.progress_callback("render_start")
                    state.apps  # Render all -- performance critical
                    if self.progress_callback:
                        self.progress_callback("render_success")
                state = self.apply_migration(state,
                                             migration,
                                             fake=fake,
                                             fake_initial=fake_initial)
                migrations_to_run.remove(migration)
            else:
                migration.mutate_state(state, preserve=False)

    def _migrate_all_backwards(self, plan, full_plan, fake):
        """
        Take a list of 2-tuples of the form (migration instance, True) and
        unapply them in reverse order they occur in the full_plan.

        Since unapplying a migration requires the project state prior to that
        migration, Django will compute the migration states before each of them
        in a first run over the plan and then unapply them in a second run over
        the plan.
        """
        migrations_to_run = {m[0] for m in plan}
        # Holds all migration states prior to the migrations being unapplied
        states = {}
        state = ProjectState(real_apps=list(self.loader.unmigrated_apps))
        if self.progress_callback:
            self.progress_callback("render_start")
        for migration, _ in full_plan:
            if not migrations_to_run:
                # We remove every migration that we applied from this set so
                # that we can bail out once the last migration has been applied
                # and don't always run until the very end of the migration
                # process.
                break
            if migration in migrations_to_run:
                if "apps" not in state.__dict__:
                    state.apps  # Render all -- performance critical
                # The state before this migration
                states[migration] = state
                # The old state keeps as-is, we continue with the new state
                state = migration.mutate_state(state, preserve=True)
                migrations_to_run.remove(migration)
            else:
                migration.mutate_state(state, preserve=False)
        if self.progress_callback:
            self.progress_callback("render_success")

        for migration, _ in plan:
            self.unapply_migration(states[migration], migration, fake=fake)

    def collect_sql(self, plan):
        """
        Takes a migration plan and returns a list of collected SQL
        statements that represent the best-efforts version of that plan.
        """
        statements = []
        state = None
        for migration, backwards in plan:
            with self.connection.schema_editor(
                    collect_sql=True) as schema_editor:
                if state is None:
                    state = self.loader.project_state(
                        (migration.app_label, migration.name), at_end=False)
                if not backwards:
                    state = migration.apply(state,
                                            schema_editor,
                                            collect_sql=True)
                else:
                    state = migration.unapply(state,
                                              schema_editor,
                                              collect_sql=True)
            statements.extend(schema_editor.collected_sql)
        return statements

    def apply_migration(self,
                        state,
                        migration,
                        fake=False,
                        fake_initial=False):
        """
        Runs a migration forwards.
        """
        if self.progress_callback:
            self.progress_callback("apply_start", migration, fake)
        if not fake:
            if fake_initial:
                # Test to see if this is an already-applied initial migration
                applied, state = self.detect_soft_applied(state, migration)
                if applied:
                    fake = True
            if not fake:
                # Alright, do it normally
                with self.connection.schema_editor() as schema_editor:
                    state = migration.apply(state, schema_editor)
        # For replacement migrations, record individual statuses
        if migration.replaces:
            for app_label, name in migration.replaces:
                self.recorder.record_applied(app_label, name)
        else:
            self.recorder.record_applied(migration.app_label, migration.name)
        # Report progress
        if self.progress_callback:
            self.progress_callback("apply_success", migration, fake)
        return state

    def unapply_migration(self, state, migration, fake=False):
        """
        Runs a migration backwards.
        """
        if self.progress_callback:
            self.progress_callback("unapply_start", migration, fake)
        if not fake:
            with self.connection.schema_editor() as schema_editor:
                state = migration.unapply(state, schema_editor)
        # For replacement migrations, record individual statuses
        if migration.replaces:
            for app_label, name in migration.replaces:
                self.recorder.record_unapplied(app_label, name)
        else:
            self.recorder.record_unapplied(migration.app_label, migration.name)
        # Report progress
        if self.progress_callback:
            self.progress_callback("unapply_success", migration, fake)
        return state

    def check_replacements(self):
        """
        Mark replacement migrations applied if their replaced set all are.

        We do this unconditionally on every migrate, rather than just when
        migrations are applied or unapplied, so as to correctly handle the case
        when a new squash migration is pushed to a deployment that already had
        all its replaced migrations applied. In this case no new migration will
        be applied, but we still want to correctly maintain the applied state
        of the squash migration.
        """
        applied = self.recorder.applied_migrations()
        for key, migration in self.loader.replacements.items():
            all_applied = all(m in applied for m in migration.replaces)
            if all_applied and key not in applied:
                self.recorder.record_applied(*key)

    def detect_soft_applied(self, project_state, migration):
        """
        Tests whether a migration has been implicitly applied - that the
        tables or columns it would create exist. This is intended only for use
        on initial migrations (as it only looks for CreateModel and AddField).
        """
        if migration.initial is None:
            # Bail if the migration isn't the first one in its app
            if any(app == migration.app_label
                   for app, name in migration.dependencies):
                return False, project_state
        elif migration.initial is False:
            # Bail if it's NOT an initial migration
            return False, project_state

        if project_state is None:
            after_state = self.loader.project_state(
                (migration.app_label, migration.name), at_end=True)
        else:
            after_state = migration.mutate_state(project_state)
        apps = after_state.apps
        found_create_model_migration = False
        found_add_field_migration = False
        existing_table_names = self.connection.introspection.table_names(
            self.connection.cursor())
        # Make sure all create model and add field operations are done
        for operation in migration.operations:
            if isinstance(operation, migrations.CreateModel):
                model = apps.get_model(migration.app_label, operation.name)
                if model._meta.swapped:
                    # We have to fetch the model to test with from the
                    # main app cache, as it's not a direct dependency.
                    model = global_apps.get_model(model._meta.swapped)
                if model._meta.proxy or not model._meta.managed:
                    continue
                if model._meta.db_table not in existing_table_names:
                    return False, project_state
                found_create_model_migration = True
            elif isinstance(operation, migrations.AddField):
                model = apps.get_model(migration.app_label,
                                       operation.model_name)
                if model._meta.swapped:
                    # We have to fetch the model to test with from the
                    # main app cache, as it's not a direct dependency.
                    model = global_apps.get_model(model._meta.swapped)
                if model._meta.proxy or not model._meta.managed:
                    continue

                table = model._meta.db_table
                field = model._meta.get_field(operation.name)

                # Handle implicit many-to-many tables created by AddField.
                if field.many_to_many:
                    if field.remote_field.through._meta.db_table not in existing_table_names:
                        return False, project_state
                    else:
                        found_add_field_migration = True
                        continue

                column_names = [
                    column.name for column in self.connection.introspection.
                    get_table_description(self.connection.cursor(), table)
                ]
                if field.column not in column_names:
                    return False, project_state
                found_add_field_migration = True
        # If we get this far and we found at least one CreateModel or AddField migration,
        # the migration is considered implicitly applied.
        return (found_create_model_migration
                or found_add_field_migration), after_state
コード例 #36
0
ファイル: makemigrations.py プロジェクト: aken7/django
    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)
        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)

        # 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)
コード例 #37
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"]
        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"]

        # 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()):
                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 app_labels:
                    if len(app_labels) == 1:
                        self.stdout.write("No changes detected in app '%s'" %
                                          app_labels.pop())
                    else:
                        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)
コード例 #38
0
import os
コード例 #39
0
class Command(makemigrations.Command):
    help = 'Runs makemigrations with some injected code that causes makemigrations to ignore combined model views, ' \
           'then adds the combined model views separately.'
    """To avoid the risk of overloading names from the makemigrations.Command class, I've prefixed all attributes
    I assign to this Command class, or an instance thereof, with '_mcv_' (short for 'Make Combined Views'). This
    convention makes it unlikely that my names will ever collide with Django's, even if updates are made to the
    Django makemigrations script."""
    def _mcv_step2_init(self, *app_labels, **options):
        self._mcv_loader = MigrationLoader(None)
        self._mcv_combined_model_nodes = {
            tup_id: migration
            for tup_id, migration in self._mcv_loader.graph.nodes.items()
            if any([
                type(op) == CreateCombinedView for op in migration.operations
            ])
        }
        self._mcv_current_combined_models = gather_combined_models()
        self._mcv_current_state = self._mcv_loader.project_state()
        self._mcv_latest_combined_model_state = self._mcv_loader.project_state(
            nodes=list(self._mcv_combined_model_nodes.keys()))

    def handle(self, *app_labels, **options):
        """A filthy hack. Can't be helped so far as I can tell because Django makes the assumption
        that, other than /the class Model/, all classes whose metaclasses inherit from ModelBase are user-defined
        models that need to have normal migration logic applied to them. This usually doesn't matter, since
        CombinedModelViews are forced to be unmanaged (see combine.base.CombinedModelViewBase). However,
        the CreateModel operation is applied even to unmanaged models. So we have to monkey patch it."""
        with patch('django.db.migrations.operations.CreateModel',
                   new=CombinedViewCheckingCreateModel):
            super(Command, self).handle(*app_labels, **options)

        #TODO: Handle wacky makemigrations options such as --dry-run.

        self._mcv_step2_init(*app_labels, **options)

        models_to_add, models_to_remove = self._mcv_get_combined_model_additions_and_removals(
        )
        app_labels_to_use = set([
            label_and_model[0] for label_and_model in models_to_add
        ]).union(
            set([label_and_model[0] for label_and_model in models_to_remove]))
        operations = {label: [] for label in app_labels_to_use}
        for app_label, model_name in models_to_add:
            model = self._mcv_current_combined_models[(app_label, model_name)]
            donors = model._combiner.donors
            renames = model._combiner.renames.deconstruct()
            operations[app_label].append(
                CreateCombinedView(model._meta.object_name, donors, renames))
        changes = {}
        for label, op_list in operations.items():
            subclass = type(str("Migration"), (Migration, ), {
                "operations": op_list,
                "dependencies": []
            })
            instance = subclass("combinedview", app_label)
            changes[label] = [instance]
        # TODO: Record model changes among models
        #I don't actually care about autodetecting anything, I just want the arrange_for_graph() method
        autodetector = MigrationAutodetector(self._mcv_loader.project_state(),
                                             ProjectState.from_apps(apps))
        changes = autodetector.arrange_for_graph(changes,
                                                 self._mcv_loader.graph)
        if changes:
            self.write_migration_files(changes)

    def _mcv_historical_combined_models(self):
        # TODO: Currently counts all combined models that EVER existed, but should instead check the state history to
        # prune out overwritten/deleted combined models
        to_return = {}
        for app_label, migration_name in self._mcv_combined_model_nodes.keys():
            migration = self._mcv_combined_model_nodes[(app_label,
                                                        migration_name)]
            model_name = [
                o for o in migration.operations
                if type(o) == CreateCombinedView
            ][0].name.lower()
            to_return[(
                app_label,
                model_name)] = self._mcv_latest_combined_model_state.models[(
                    app_label, model_name)]
        return to_return

    def _mcv_get_combined_model_additions_and_removals(self):
        #returns a 2-tuple of sets of (app_label, model) 2-tuples
        historic_combined_model_identifiers = set(
            self._mcv_historical_combined_models().keys())
        current_combined_model_identifiers = set(
            self._mcv_current_combined_models.keys())
        models_to_add = current_combined_model_identifiers - historic_combined_model_identifiers
        models_to_remove = historic_combined_model_identifiers - current_combined_model_identifiers
        return (models_to_add, models_to_remove)
コード例 #40
0
class TestAlterSortedManyToManyFieldOperation(TransactionTestCase):
    def setUp(self):
        from django.db.migrations.executor import MigrationExecutor
        from django.db.migrations.loader import MigrationLoader
        from django.db import connection

        self.migration_executor = MigrationExecutor(connection)
        self.migration_loader = MigrationLoader(connection)
        self.state_0001 = self.migration_loader.project_state(
            ('altersortedmanytomanyfield_tests', '0001_initial'))
        self.state_0002 = self.migration_loader.project_state(
            ('altersortedmanytomanyfield_tests', '0002_alter_m2m_fields'))
        self.state_0001_apps = self.state_0001.render()
        self.state_0002_apps = self.state_0002.render()

        # Make sure we are at the latest migration when starting the test.
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests')

    def test_apply_migrations_backwards(self):
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests', '0001')

    def test_operation_m2m_to_sorted_m2m(self):

        # Let's start with state after 0001
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests', '0001')

        Target = self.state_0001_apps.get_model(
            'altersortedmanytomanyfield_tests', 'target')
        M2MToSortedM2M = self.state_0001_apps.get_model(
            'altersortedmanytomanyfield_tests', 'm2mtosortedm2m')

        t1 = Target.objects.create(pk=1)
        t2 = Target.objects.create(pk=2)
        t3 = Target.objects.create(pk=3)

        field = M2MToSortedM2M._meta.get_field_by_name('m2m')[0]
        through_model = field.rel.through
        # No ordering is in place.
        self.assertTrue(not through_model._meta.ordering)


        instance = M2MToSortedM2M.objects.create(pk=1)
        instance.m2m.add(t3)
        instance.m2m.add(t1)
        instance.m2m.add(t2)

        # We cannot assume any particular order now.

        # Migrate to state 0002, then we should be able to apply order.
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests', '0002')

        Target = self.state_0002_apps.get_model(
            'altersortedmanytomanyfield_tests', 'target')
        M2MToSortedM2M = self.state_0002_apps.get_model(
            'altersortedmanytomanyfield_tests', 'm2mtosortedm2m')

        t1 = Target.objects.get(pk=1)
        t2 = Target.objects.get(pk=2)
        t3 = Target.objects.get(pk=3)

        field = M2MToSortedM2M._meta.get_field_by_name('m2m')[0]
        through_model = field.rel.through
        # Now, ordering is there.
        self.assertTrue(list(through_model._meta.ordering), ['sort_value'])

        instance = M2MToSortedM2M.objects.get(pk=1)
        self.assertEqual(list(instance.m2m.order_by('pk')), [t1, t2, t3])

        instance.m2m = [t3, t1, t2]

        self.assertEqual(list(instance.m2m.all()), [t3, t1, t2])

        instance.m2m.remove(t1)
        instance.m2m.remove(t2)
        instance.m2m.add(t2)
        instance.m2m.add(t1)

        self.assertEqual(list(instance.m2m.all()), [t3, t2, t1])
コード例 #41
0
ファイル: makemigrations.py プロジェクト: 912/M-new
    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)
        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)

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

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

        # 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)
コード例 #42
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)

        # Raise an error if any migrations are applied before their dependencies.
        for db in connections:
            loader.check_consistent_history(connections[db])

        # 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)
コード例 #43
0
class TestAlterSortedManyToManyFieldOperation(TransactionTestCase):
    def setUp(self):
        from django.db.migrations.executor import MigrationExecutor
        from django.db.migrations.loader import MigrationLoader
        from django.db import connection

        self.migration_executor = MigrationExecutor(connection)
        self.migration_loader = MigrationLoader(connection)
        self.state_0001 = self.migration_loader.project_state(
            ('altersortedmanytomanyfield_tests', '0001_initial'))
        self.state_0002 = self.migration_loader.project_state(
            ('altersortedmanytomanyfield_tests', '0002_alter_m2m_fields'))
        self.state_0001_apps = get_apps_from_state(self.state_0001)
        self.state_0002_apps = get_apps_from_state(self.state_0002)

        # Make sure we are at the latest migration when starting the test.
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests')

    def test_apply_migrations_backwards(self):
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests', '0001')

    def test_operation_m2m_to_sorted_m2m(self):

        # Let's start with state after 0001
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests', '0001')

        Target = self.state_0001_apps.get_model(
            'altersortedmanytomanyfield_tests', 'target')
        M2MToSortedM2M = self.state_0001_apps.get_model(
            'altersortedmanytomanyfield_tests', 'm2mtosortedm2m')

        t1 = Target.objects.create(pk=1)
        t2 = Target.objects.create(pk=2)
        t3 = Target.objects.create(pk=3)

        field = get_field(M2MToSortedM2M,'m2m')
        through_model = get_rel(field).through
        # No ordering is in place.
        self.assertTrue(not through_model._meta.ordering)


        instance = M2MToSortedM2M.objects.create(pk=1)
        instance.m2m.add(t3)
        instance.m2m.add(t1)
        instance.m2m.add(t2)

        # We cannot assume any particular order now.

        # Migrate to state 0002, then we should be able to apply order.
        with capture_stdout():
            call_command('migrate', 'altersortedmanytomanyfield_tests', '0002')

        Target = self.state_0002_apps.get_model(
            'altersortedmanytomanyfield_tests', 'target')
        M2MToSortedM2M = self.state_0002_apps.get_model(
            'altersortedmanytomanyfield_tests', 'm2mtosortedm2m')

        t1 = Target.objects.get(pk=1)
        t2 = Target.objects.get(pk=2)
        t3 = Target.objects.get(pk=3)

        field = get_field(M2MToSortedM2M,'m2m')
        through_model = get_rel(field).through
        # Now, ordering is there.
        self.assertTrue(list(through_model._meta.ordering), ['sort_value'])

        instance = M2MToSortedM2M.objects.get(pk=1)
        self.assertEqual(list(instance.m2m.order_by('pk')), [t1, t2, t3])

        m2m_set(instance, "m2m", [t3, t1, t2])

        self.assertEqual(list(instance.m2m.all()), [t3, t1, t2])

        instance.m2m.remove(t1)
        instance.m2m.remove(t2)
        instance.m2m.add(t2)
        instance.m2m.add(t1)

        self.assertEqual(list(instance.m2m.all()), [t3, t2, t1])
コード例 #44
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.replace_all = options['replace_all']
        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:
                if '.' in app_label:
                    self.stderr.write(
                        "'%s' is not a valid app label. Did you mean '%s'?" % (
                            app_label,
                            app_label.split('.')[-1],
                        ))
                else:
                    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)

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

        if self.replace_all:
            replace_list = [
                migration for migration in loader.graph.nodes.values()
            ]
            temp_nodes = loader.graph.nodes

            loader.graph.nodes = {
                k: v
                for (k, v) in loader.graph.nodes.items()
                if k[0] not in app_labels
            }

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

            loader.graph.nodes = temp_nodes

        else:
            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.stdout.write("No changes detected in app '%s'" %
                                          app_labels.pop())
                    else:
                        self.stdout.write("No changes detected in apps '%s'" %
                                          ("', '".join(app_labels)))
                else:
                    self.stdout.write("No changes detected")
        else:
            if self.replace_all:
                for app_label, app_migrations in changes.items():
                    for app_migration in app_migrations:
                        app_migration.replaces = \
                            [
                                (migration.app_label, migration.name)
                                for migration in replace_list
                                if migration.app_label == app_label
                            ]
                        app_migration.dependencies = [
                            dependency
                            for dependency in app_migration.dependencies
                            if dependency not in app_migration.replaces
                        ]

            self.write_migration_files(changes)
            if check_changes:
                sys.exit(1)
コード例 #45
0
ファイル: makemigrations.py プロジェクト: yephper/django
    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)