Commit 492537ac authored by Carl Meyer's avatar Carl Meyer
Browse files

Fixed #24628 -- Fixed applied status for squashed migrations.

parent 335fc44f
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ class MigrationExecutor(object):
                self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial)
            else:
                self.unapply_migration(states[migration], migration, fake=fake)
        self.check_replacements()

    def collect_sql(self, plan):
        """
@@ -176,6 +177,16 @@ class MigrationExecutor(object):
            self.progress_callback("unapply_success", migration, fake)
        return state

    def check_replacements(self):
        """
        Mark replacement migrations applied if their replaced set all are.
        """
        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
+2 −0
Original line number Diff line number Diff line
@@ -251,6 +251,8 @@ class MigrationLoader(object):
            # Mark the replacement as applied if all its replaced ones are
            if all(applied_statuses):
                self.applied_migrations.add(key)
        # Store the replacement migrations for later checks
        self.replacements = replacing
        # Finally, make a graph and load everything into it
        self.graph = MigrationGraph()
        for key, migration in normal.items():
+3 −0
Original line number Diff line number Diff line
@@ -47,3 +47,6 @@ Bugfixes
* Fixed a crash when loading squashed migrations from two apps with a
  dependency between them, where the dependent app's replaced migrations are
  partially applied (:ticket:`24895`).

* Fixed recording of applied status for squashed (replacement) migrations
  (:ticket:`24628`).
+26 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ from django.apps.registry import apps as global_apps
from django.db import connection
from django.db.migrations.executor import MigrationExecutor
from django.db.migrations.graph import MigrationGraph
from django.db.migrations.recorder import MigrationRecorder
from django.db.utils import DatabaseError
from django.test import TestCase, modify_settings, override_settings

@@ -411,6 +412,31 @@ class ExecutorTests(MigrationTestBase):
            self.assertTableNotExists("author_app_author")
            self.assertTableNotExists("book_app_book")

    @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"})
    def test_apply_all_replaced_marks_replacement_as_applied(self):
        """
        Applying all replaced migrations marks the replacement as applied.

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


class FakeLoader(object):
    def __init__(self, graph, applied):