예제 #1
0
 def test_apply(self):
     """
     Tests marking migrations as applied/unapplied.
     """
     recorder = MigrationRecorder(connection)
     self.assertEqual(
         set((x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"),
         set(),
     )
     recorder.record_applied("myapp", "0432_ponies")
     self.assertEqual(
         set((x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"),
         {("myapp", "0432_ponies")},
     )
     # That should not affect records of another database
     recorder_other = MigrationRecorder(connections['other'])
     self.assertEqual(
         set((x, y) for (x, y) in recorder_other.applied_migrations() if x == "myapp"),
         set(),
     )
     recorder.record_unapplied("myapp", "0432_ponies")
     self.assertEqual(
         set((x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"),
         set(),
     )
예제 #2
0
 def test_apply(self):
     """
     Tests marking migrations as applied/unapplied.
     """
     recorder = MigrationRecorder(connection)
     self.assertEqual(
         set((x, y) for (x, y) in recorder.applied_migrations()
             if x == "myapp"),
         set(),
     )
     recorder.record_applied("myapp", "0432_ponies")
     self.assertEqual(
         set((x, y) for (x, y) in recorder.applied_migrations()
             if x == "myapp"),
         {("myapp", "0432_ponies")},
     )
     # That should not affect records of another database
     recorder_other = MigrationRecorder(connections['other'])
     self.assertEqual(
         set((x, y) for (x, y) in recorder_other.applied_migrations()
             if x == "myapp"),
         set(),
     )
     recorder.record_unapplied("myapp", "0432_ponies")
     self.assertEqual(
         set((x, y) for (x, y) in recorder.applied_migrations()
             if x == "myapp"),
         set(),
     )
예제 #3
0
 def test_check_consistent_history(self):
     loader = MigrationLoader(connection=None)
     loader.check_consistent_history(connection)
     recorder = MigrationRecorder(connection)
     recorder.record_applied('migrations', '0002_second')
     msg = "Migration migrations.0002_second is applied before its dependency migrations.0001_initial"
     with self.assertRaisesMessage(InconsistentMigrationHistory, msg):
         loader.check_consistent_history(connection)
예제 #4
0
 def test_check_consistent_history(self):
     loader = MigrationLoader(connection=None)
     loader.check_consistent_history(connection)
     recorder = MigrationRecorder(connection)
     recorder.record_applied('migrations', '0002_second')
     msg = "Migration migrations.0002_second is applied before its dependency migrations.0001_initial"
     with self.assertRaisesMessage(InconsistentMigrationHistory, msg):
         loader.check_consistent_history(connection)
예제 #5
0
    def test_loading_squashed_ref_squashed(self):
        "Tests loading a squashed migration with a new migration referencing it"
        r"""
        The sample migrations are structured like this:

        app_1       1 --> 2 ---------------------*--> 3        *--> 4
                     \                          /             /
                      *-------------------*----/--> 2_sq_3 --*
                       \                 /    /
        =============== \ ============= / == / ======================
        app_2            *--> 1_sq_2 --*    /
                          \                /
                           *--> 1 --> 2 --*

        Where 2_sq_3 is a replacing migration for 2 and 3 in app_1,
        as 1_sq_2 is a replacing migration for 1 and 2 in app_2.
        """

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        # Load with nothing applied: both migrations squashed.
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app1', '1_auto'),
            ('app2', '1_squashed_2'),
            ('app1', '2_squashed_3'),
            ('app1', '4_auto'),
        }
        self.assertEqual(plan, expected_plan)

        # Fake-apply a few from app1: unsquashes migration in app1.
        recorder.record_applied('app1', '1_auto')
        recorder.record_applied('app1', '2_auto')
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app2', '1_squashed_2'),
            ('app1', '3_auto'),
            ('app1', '4_auto'),
        }
        self.assertEqual(plan, expected_plan)

        # Fake-apply one from app2: unsquashes migration in app2 too.
        recorder.record_applied('app2', '1_auto')
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app2', '2_auto'),
            ('app1', '3_auto'),
            ('app1', '4_auto'),
        }
        self.assertEqual(plan, expected_plan)
예제 #6
0
    def test_loading_squashed_ref_squashed(self):
        "Tests loading a squashed migration with a new migration referencing it"
        """
        The sample migrations are structred like this:

        app_1       1 --> 2 ---------------------*--> 3        *--> 4
                     \                          /             /
                      *-------------------*----/--> 2_sq_3 --*
                       \                 /    /
        =============== \ ============= / == / ======================
        app_2            *--> 1_sq_2 --*    /
                          \                /
                           *--> 1 --> 2 --*

        Where 2_sq_3 is a replacing migration for 2 and 3 in app_1,
        as 1_sq_2 is a replacing migration for 1 and 2 in app_2.
        """

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        # Load with nothing applied: both migrations squashed.
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app1', '1_auto'),
            ('app2', '1_squashed_2'),
            ('app1', '2_squashed_3'),
            ('app1', '4_auto'),
        }
        self.assertEqual(plan, expected_plan)

        # Fake-apply a few from app1: unsquashes migration in app1.
        recorder.record_applied('app1', '1_auto')
        recorder.record_applied('app1', '2_auto')
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app2', '1_squashed_2'),
            ('app1', '3_auto'),
            ('app1', '4_auto'),
        }
        self.assertEqual(plan, expected_plan)

        # Fake-apply one from app2: unsquashes migration in app2 too.
        recorder.record_applied('app2', '1_auto')
        loader.build_graph()
        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app2', '2_auto'),
            ('app1', '3_auto'),
            ('app1', '4_auto'),
        }
        self.assertEqual(plan, expected_plan)
예제 #7
0
 def test_loading_squashed(self):
     "Tests loading a squashed migration"
     migration_loader = MigrationLoader(connection)
     recorder = MigrationRecorder(connection)
     # Loading with nothing applied should just give us the one node
     self.assertEqual(len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]), 1)
     # However, fake-apply one migration and it should now use the old two
     recorder.record_applied("migrations", "0001_initial")
     migration_loader.build_graph()
     self.assertEqual(len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]), 2)
     recorder.flush()
예제 #8
0
 def test_migrate_record_squashed(self):
     """
     Running migrate for a squashed migration should record as run
     if all of the replaced migrations have been run (#25231).
     """
     recorder = MigrationRecorder(connection)
     recorder.record_applied("migrations", "0001_initial")
     recorder.record_applied("migrations", "0002_second")
     out = six.StringIO()
     call_command("migrate", "migrations", verbosity=0)
     call_command("showmigrations", "migrations", stdout=out, no_color=True)
     self.assertEqual("migrations\n" " [x] 0001_squashed_0002 (2 squashed migrations)\n", out.getvalue().lower())
     self.assertIn(("migrations", "0001_squashed_0002"), recorder.applied_migrations())
예제 #9
0
    def test_loading_squashed_complex_multi_apps_partially_applied(self):
        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        recorder.record_applied('app1', '1_auto')
        recorder.record_applied('app1', '2_auto')
        loader.build_graph()

        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app2', '1_squashed_2'),
            ('app1', '3_auto'),
            ('app1', '4_auto'),
        }

        self.assertEqual(plan, expected_plan)
예제 #10
0
    def test_loading_squashed_complex_multi_apps_partially_applied(self):
        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        recorder.record_applied('app1', '1_auto')
        recorder.record_applied('app1', '2_auto')
        loader.build_graph()

        plan = set(loader.graph.forwards_plan(('app1', '4_auto')))
        plan = plan - loader.applied_migrations
        expected_plan = {
            ('app2', '1_squashed_2'),
            ('app1', '3_auto'),
            ('app1', '4_auto'),
        }

        self.assertEqual(plan, expected_plan)
예제 #11
0
    def test_migrate_marks_replacement_applied_even_if_it_did_nothing(self):
        """
        A new squash migration will be marked as applied even if all its
        replaced migrations were previously already applied (#24628).
        """
        recorder = MigrationRecorder(connection)
        # Record all replaced migrations as applied
        recorder.record_applied("migrations", "0001_initial")
        recorder.record_applied("migrations", "0002_second")
        executor = MigrationExecutor(connection)
        executor.migrate([("migrations", "0001_squashed_0002")])

        # Because 0001 and 0002 are both applied, even though this migrate run
        # didn't apply anything new, their squashed replacement should be
        # marked as applied.
        self.assertIn(("migrations", "0001_squashed_0002"), recorder.applied_migrations())
예제 #12
0
 def test_migrate_record_squashed(self):
     """
     Running migrate for a squashed migration should record as run
     if all of the replaced migrations have been run (#25231).
     """
     recorder = MigrationRecorder(connection)
     recorder.record_applied("migrations", "0001_initial")
     recorder.record_applied("migrations", "0002_second")
     out = six.StringIO()
     call_command("migrate", "migrations", verbosity=0)
     call_command("showmigrations", "migrations", stdout=out, no_color=True)
     self.assertEqual(
         'migrations\n'
         ' [x] 0001_squashed_0002 (2 squashed migrations)\n',
         out.getvalue().lower())
     self.assertIn(("migrations", "0001_squashed_0002"),
                   recorder.applied_migrations())
예제 #13
0
 def test_loading_squashed(self):
     "Tests loading a squashed migration"
     migration_loader = MigrationLoader(connection)
     recorder = MigrationRecorder(connection)
     self.addCleanup(recorder.flush)
     # Loading with nothing applied should just give us the one node
     self.assertEqual(
         len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]),
         1,
     )
     # However, fake-apply one migration and it should now use the old two
     recorder.record_applied("migrations", "0001_initial")
     migration_loader.build_graph()
     self.assertEqual(
         len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]),
         2,
     )
예제 #14
0
    def test_apply_all_replaced_marks_replacement_as_applied(self):
        """
        Applying all replaced migrations marks replacement as applied (#24628).
        """
        recorder = MigrationRecorder(connection)
        # Place the database in a state where the replaced migrations are
        # partially applied: 0001 is applied, 0002 is not.
        recorder.record_applied("migrations", "0001_initial")
        executor = MigrationExecutor(connection)
        # Use fake because we don't actually have the first migration
        # applied, so the second will fail. And there's no need to actually
        # create/modify tables here, we're just testing the
        # MigrationRecord, which works the same with or without fake.
        executor.migrate([("migrations", "0002_second")], fake=True)

        # Because we've now applied 0001 and 0002 both, their squashed
        # replacement should be marked as applied.
        self.assertIn(("migrations", "0001_squashed_0002"), recorder.applied_migrations())
예제 #15
0
    def test_migrate_marks_replacement_applied_even_if_it_did_nothing(self):
        """
        A new squash migration will be marked as applied even if all its
        replaced migrations were previously already applied (#24628).
        """
        recorder = MigrationRecorder(connection)
        # Record all replaced migrations as applied
        recorder.record_applied("migrations", "0001_initial")
        recorder.record_applied("migrations", "0002_second")
        executor = MigrationExecutor(connection)
        executor.migrate([("migrations", "0001_squashed_0002")])

        # Because 0001 and 0002 are both applied, even though this migrate run
        # didn't apply anything new, their squashed replacement should be
        # marked as applied.
        self.assertIn(
            ("migrations", "0001_squashed_0002"),
            recorder.applied_migrations(),
        )
예제 #16
0
 def test_apply(self):
     """
     Tests marking migrations as applied/unapplied.
     """
     recorder = MigrationRecorder(connection)
     self.assertEqual(
         recorder.applied_migrations(),
         set(),
     )
     recorder.record_applied("myapp", "0432_ponies")
     self.assertEqual(
         recorder.applied_migrations(),
         set([("myapp", "0432_ponies")]),
     )
     recorder.record_unapplied("myapp", "0432_ponies")
     self.assertEqual(
         recorder.applied_migrations(),
         set(),
     )
예제 #17
0
 def test_apply(self):
     """
     Tests marking migrations as applied/unapplied.
     """
     recorder = MigrationRecorder(connection)
     self.assertEqual(
         recorder.applied_migrations(),
         set(),
     )
     recorder.record_applied("myapp", "0432_ponies")
     self.assertEqual(
         recorder.applied_migrations(),
         set([("myapp", "0432_ponies")]),
     )
     recorder.record_unapplied("myapp", "0432_ponies")
     self.assertEqual(
         recorder.applied_migrations(),
         set(),
     )
예제 #18
0
    def test_apply_all_replaced_marks_replacement_as_applied(self):
        """
        Applying all replaced migrations marks replacement as applied (#24628).
        """
        recorder = MigrationRecorder(connection)
        # Place the database in a state where the replaced migrations are
        # partially applied: 0001 is applied, 0002 is not.
        recorder.record_applied("migrations", "0001_initial")
        executor = MigrationExecutor(connection)
        # Use fake because we don't actually have the first migration
        # applied, so the second will fail. And there's no need to actually
        # create/modify tables here, we're just testing the
        # MigrationRecord, which works the same with or without fake.
        executor.migrate([("migrations", "0002_second")], fake=True)

        # Because we've now applied 0001 and 0002 both, their squashed
        # replacement should be marked as applied.
        self.assertIn(
            ("migrations", "0001_squashed_0002"),
            recorder.applied_migrations(),
        )
예제 #19
0
 def test_check_consistent_history_squashed(self):
     """
     MigrationLoader.check_consistent_history() should ignore unapplied
     squashed migrations that have all of their `replaces` applied.
     """
     loader = MigrationLoader(connection=None)
     recorder = MigrationRecorder(connection)
     recorder.record_applied('migrations', '0001_initial')
     recorder.record_applied('migrations', '0002_second')
     loader.check_consistent_history(connection)
     recorder.record_applied('migrations', '0003_third')
     loader.check_consistent_history(connection)
예제 #20
0
 def test_check_consistent_history_squashed(self):
     """
     MigrationLoader.check_consistent_history() should ignore unapplied
     squashed migrations that have all of their `replaces` applied.
     """
     loader = MigrationLoader(connection=None)
     recorder = MigrationRecorder(connection)
     recorder.record_applied('migrations', '0001_initial')
     recorder.record_applied('migrations', '0002_second')
     loader.check_consistent_history(connection)
     recorder.record_applied('migrations', '0003_third')
     loader.check_consistent_history(connection)
예제 #21
0
    def test_loading_squashed_erroneous(self):
        "Tests loading a complex but erroneous set of squashed migrations"

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        def num_nodes():
            plan = set(loader.graph.forwards_plan(('migrations', '7_auto')))
            return len(plan - loader.applied_migrations)

        # Empty database: use squashed migration
        loader.build_graph()
        self.assertEqual(num_nodes(), 5)

        # Starting at 1 or 2 should use the squashed migration too
        recorder.record_applied("migrations", "1_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        recorder.record_applied("migrations", "2_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        # However, starting at 3 or 4 we'd need to use non-existing migrations
        msg = (
            "Migration migrations.6_auto depends on nonexistent node ('migrations', '5_auto'). "
            "Django tried to replace migration migrations.5_auto with any of "
            "[migrations.3_squashed_5] but wasn't able to because some of the replaced "
            "migrations are already applied.")

        recorder.record_applied("migrations", "3_auto")
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.build_graph()

        recorder.record_applied("migrations", "4_auto")
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.build_graph()

        # Starting at 5 to 7 we are passed the squashed migrations
        recorder.record_applied("migrations", "5_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 2)

        recorder.record_applied("migrations", "6_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 1)

        recorder.record_applied("migrations", "7_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 0)
예제 #22
0
    from django.core.exceptions import ImproperlyConfigured
    from django.contrib.contenttypes.models import ContentType
    from django.db import DEFAULT_DB_ALIAS, connections, migrations, models
    from django.db.migrations.recorder import MigrationRecorder
    import django.db.models.deletion
except ImportError as error:
    print(error)

# Fix a bad `social_django` migration.
try:
    BAD_MIGRATION = ('default', '0004_auto_20160423_0400')
    recorder = MigrationRecorder(connections[DEFAULT_DB_ALIAS])
    applied = recorder.applied_migrations()

    if applied and (BAD_MIGRATION not in applied):
        recorder.record_applied(*BAD_MIGRATION)
except (NameError, ImproperlyConfigured) as error:
    print(error)


class Migration(migrations.Migration):

    dependencies = [
        ('api_v3', '0008_v1_to_v2_attachments'),
    ]

    operations = [
        migrations.RunPython(lambda _a, _s: ContentType.objects.filter(
            app_label='accounts', model='profile').update(app_label='api_v3')),
        migrations.CreateModel(
            name='Subscriber',
예제 #23
0
    def test_loading_squashed_erroneous(self):
        "Tests loading a complex but erroneous set of squashed migrations"

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        def num_nodes():
            plan = set(loader.graph.forwards_plan(('migrations', '7_auto')))
            return len(plan - loader.applied_migrations)

        # Empty database: use squashed migration
        loader.build_graph()
        self.assertEqual(num_nodes(), 5)

        # Starting at 1 or 2 should use the squashed migration too
        recorder.record_applied("migrations", "1_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        recorder.record_applied("migrations", "2_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        # However, starting at 3 or 4, nonexistent migrations would be needed.
        msg = ("Migration migrations.6_auto depends on nonexistent node ('migrations', '5_auto'). "
               "Django tried to replace migration migrations.5_auto with any of "
               "[migrations.3_squashed_5] but wasn't able to because some of the replaced "
               "migrations are already applied.")

        recorder.record_applied("migrations", "3_auto")
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.build_graph()

        recorder.record_applied("migrations", "4_auto")
        with self.assertRaisesMessage(NodeNotFoundError, msg):
            loader.build_graph()

        # Starting at 5 to 7 we are passed the squashed migrations
        recorder.record_applied("migrations", "5_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 2)

        recorder.record_applied("migrations", "6_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 1)

        recorder.record_applied("migrations", "7_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 0)
예제 #24
0
from django.db import models, migrations, DEFAULT_DB_ALIAS, connections
from django.db.migrations.recorder import MigrationRecorder
import django.db.models.deletion
from django.conf import settings

connection = connections[DEFAULT_DB_ALIAS]
recorder = MigrationRecorder(connection)
linaro_django_xmlrpc_applied = False
lava_scheduler_app_applied = False
for app, name in recorder.applied_migrations():
    if app == "linaro_django_xmlrpc" and name == "0001_initial":
        linaro_django_xmlrpc_applied = True
    if app == "lava_scheduler_app" and name == "0001_initial":
        lava_scheduler_app_applied = True
if not linaro_django_xmlrpc_applied and lava_scheduler_app_applied:
    recorder.record_applied("linaro_django_xmlrpc", "0001_initial")


class Migration(migrations.Migration):

    dependencies = [
        ("auth", "0001_initial"),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ("linaro_django_xmlrpc", "__first__"),
    ]

    operations = [
        migrations.CreateModel(
            name="DefaultDeviceOwner",
            fields=[
                (
예제 #25
0
    def test_loading_squashed_complex(self):
        "Tests loading a complex set of squashed migrations"

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        def num_nodes():
            plan = set(loader.graph.forwards_plan(('migrations', '7_auto')))
            return len(plan - loader.applied_migrations)

        # Empty database: use squashed migration
        loader.build_graph()
        self.assertEqual(num_nodes(), 5)

        # Starting at 1 or 2 should use the squashed migration too
        recorder.record_applied("migrations", "1_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        recorder.record_applied("migrations", "2_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        # However, starting at 3 to 5 cannot use the squashed migration
        recorder.record_applied("migrations", "3_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        recorder.record_applied("migrations", "4_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        # Starting at 5 to 7 we are passed the squashed migrations
        recorder.record_applied("migrations", "5_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 2)

        recorder.record_applied("migrations", "6_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 1)

        recorder.record_applied("migrations", "7_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 0)
예제 #26
0
from django.db.migrations.recorder import MigrationRecorder
import django.db.models.deletion
from django.conf import settings


connection = connections[DEFAULT_DB_ALIAS]
recorder = MigrationRecorder(connection)
linaro_django_xmlrpc_applied = False
lava_scheduler_app_applied = False
for app, name in recorder.applied_migrations():
    if app == 'linaro_django_xmlrpc' and name == '0001_initial':
        linaro_django_xmlrpc_applied = True
    if app == 'lava_scheduler_app' and name == '0001_initial':
        lava_scheduler_app_applied = True
if not linaro_django_xmlrpc_applied and lava_scheduler_app_applied:
    recorder.record_applied('linaro_django_xmlrpc', '0001_initial')


class Migration(migrations.Migration):

    dependencies = [
        ('auth', '0001_initial'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('linaro_django_xmlrpc', '__first__'),
        ('dashboard_app', '__first__'),
    ]

    operations = [
        migrations.CreateModel(
            name='DefaultDeviceOwner',
            fields=[
예제 #27
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
예제 #28
0
    def test_loading_squashed_complex(self):
        "Tests loading a complex set of squashed migrations"

        loader = MigrationLoader(connection)
        recorder = MigrationRecorder(connection)
        self.addCleanup(recorder.flush)

        def num_nodes():
            plan = set(loader.graph.forwards_plan(('migrations', '7_auto')))
            return len(plan - loader.applied_migrations)

        # Empty database: use squashed migration
        loader.build_graph()
        self.assertEqual(num_nodes(), 5)

        # Starting at 1 or 2 should use the squashed migration too
        recorder.record_applied("migrations", "1_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        recorder.record_applied("migrations", "2_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        # However, starting at 3 to 5 cannot use the squashed migration
        recorder.record_applied("migrations", "3_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 4)

        recorder.record_applied("migrations", "4_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 3)

        # Starting at 5 to 7 we are passed the squashed migrations
        recorder.record_applied("migrations", "5_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 2)

        recorder.record_applied("migrations", "6_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 1)

        recorder.record_applied("migrations", "7_auto")
        loader.build_graph()
        self.assertEqual(num_nodes(), 0)
예제 #29
0
def fake_initial_users_migrations():
    recorder = MigrationRecorder(connection)
    recorder.record_applied("users", "0001_initial")
예제 #30
0
from django.db.migrations.recorder import MigrationRecorder
import django.db.models.deletion
from django.conf import settings
import lava_scheduler_app.models

connection = connections[DEFAULT_DB_ALIAS]
recorder = MigrationRecorder(connection)
linaro_django_xmlrpc_applied = False
lava_scheduler_app_applied = False
for app, name in recorder.applied_migrations():
    if app == 'linaro_django_xmlrpc' and name == '0001_initial':
        linaro_django_xmlrpc_applied = True
    if app == 'lava_scheduler_app' and name == '0001_initial':
        lava_scheduler_app_applied = True
if not linaro_django_xmlrpc_applied and lava_scheduler_app_applied:
    recorder.record_applied('linaro_django_xmlrpc', '0001_initial')


class Migration(migrations.Migration):

    dependencies = [
        ('auth', '0001_initial'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('linaro_django_xmlrpc', '__first__'),
        ('dashboard_app', '__first__'),
    ]

    operations = [
        migrations.CreateModel(
            name='DefaultDeviceOwner',
            fields=[