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

This commit is contained in:
Carl Meyer 2015-06-02 14:23:07 -06:00
parent 335fc44f68
commit 492537ac18
4 changed files with 42 additions and 0 deletions

View File

@ -110,6 +110,7 @@ class MigrationExecutor(object):
self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial) self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial)
else: else:
self.unapply_migration(states[migration], migration, fake=fake) self.unapply_migration(states[migration], migration, fake=fake)
self.check_replacements()
def collect_sql(self, plan): def collect_sql(self, plan):
""" """
@ -176,6 +177,16 @@ class MigrationExecutor(object):
self.progress_callback("unapply_success", migration, fake) self.progress_callback("unapply_success", migration, fake)
return state 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): def detect_soft_applied(self, project_state, migration):
""" """
Tests whether a migration has been implicitly applied - that the Tests whether a migration has been implicitly applied - that the

View File

@ -251,6 +251,8 @@ class MigrationLoader(object):
# Mark the replacement as applied if all its replaced ones are # Mark the replacement as applied if all its replaced ones are
if all(applied_statuses): if all(applied_statuses):
self.applied_migrations.add(key) self.applied_migrations.add(key)
# Store the replacement migrations for later checks
self.replacements = replacing
# Finally, make a graph and load everything into it # Finally, make a graph and load everything into it
self.graph = MigrationGraph() self.graph = MigrationGraph()
for key, migration in normal.items(): for key, migration in normal.items():

View File

@ -47,3 +47,6 @@ Bugfixes
* Fixed a crash when loading squashed migrations from two apps with a * Fixed a crash when loading squashed migrations from two apps with a
dependency between them, where the dependent app's replaced migrations are dependency between them, where the dependent app's replaced migrations are
partially applied (:ticket:`24895`). partially applied (:ticket:`24895`).
* Fixed recording of applied status for squashed (replacement) migrations
(:ticket:`24628`).

View File

@ -2,6 +2,7 @@ from django.apps.registry import apps as global_apps
from django.db import connection from django.db import connection
from django.db.migrations.executor import MigrationExecutor from django.db.migrations.executor import MigrationExecutor
from django.db.migrations.graph import MigrationGraph from django.db.migrations.graph import MigrationGraph
from django.db.migrations.recorder import MigrationRecorder
from django.db.utils import DatabaseError from django.db.utils import DatabaseError
from django.test import TestCase, modify_settings, override_settings from django.test import TestCase, modify_settings, override_settings
@ -411,6 +412,31 @@ class ExecutorTests(MigrationTestBase):
self.assertTableNotExists("author_app_author") self.assertTableNotExists("author_app_author")
self.assertTableNotExists("book_app_book") 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): class FakeLoader(object):
def __init__(self, graph, applied): def __init__(self, graph, applied):