From ea70b4ba4ead55acdebc432d2da651c205e3be3c Mon Sep 17 00:00:00 2001 From: Georgi Yanchev Date: Thu, 13 Nov 2025 13:38:16 -0500 Subject: [PATCH] Check applied replaced migrations recursively. --- django/db/migrations/executor.py | 3 +-- django/db/migrations/loader.py | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/django/db/migrations/executor.py b/django/db/migrations/executor.py index ebfe8572fecb..7dba2e26d7e8 100644 --- a/django/db/migrations/executor.py +++ b/django/db/migrations/executor.py @@ -302,8 +302,7 @@ def check_replacements(self): """ 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: + if key not in applied and self.loader.all_replaced_applied(key, applied): self.recorder.record_applied(*key) def detect_soft_applied(self, project_state, migration): diff --git a/django/db/migrations/loader.py b/django/db/migrations/loader.py index 207be657b427..ca40c6d7f182 100644 --- a/django/db/migrations/loader.py +++ b/django/db/migrations/loader.py @@ -352,11 +352,8 @@ def check_consistent_history(self, connection): if parent not in applied: # Skip unapplied squashed migrations that have all of their # `replaces` applied. - if parent in self.replacements: - if all( - m in applied for m in self.replacements[parent].replaces - ): - continue + if self.all_replaced_applied(parent.key, applied): + continue raise InconsistentMigrationHistory( "Migration {}.{} is applied before its dependency " "{}.{} on database '{}'.".format( @@ -368,6 +365,20 @@ def check_consistent_history(self, connection): ) ) + def all_replaced_applied(self, migration_key, applied): + """ + Checks (recursively) whether all replaced migrations are applied. + """ + if migration_key in self.replacements: + for replaced_key in self.replacements[migration_key].replaces: + if replaced_key not in applied and not self.all_replaced_applied( + replaced_key, applied + ): + return False + return True + + return False + def detect_conflicts(self): """ Look through the loaded graph and detect any conflicts - apps