﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36168	Backwards migration to replaced migration when other app has squashed migrations can lead to FieldDoesNotExist error due to incorrect state	Klaas van Schelven	houston0222	"I managed to debug my way to the broken part of the code, but did not manage to find a nice clean reproduction. Hence the following report starts with broken code and only then explains the problem, which is the unusual order.

[https://github.com/django/django/blob/b1324a680add78de24c763911d0eefa19b9263bc/django/db/migrations/executor.py#L49 here's the broken code]

This is wrong because it mutates the state on the `MigrationExecutor`, i.e. in certain conditions a different graph (without the pruning of `replace_migrations`) is set ""globally"" on the `MigrationExecutor`.

But `migration_plan` (the function of which the broken code is part) is called _twice_ in at least some flows. In particular: first as part of `migrate`, and then as part of `_create_project_state`. This means that, for flows where the broken code is called in the first call to `migration_plan` (for presumably good reasons, as per the comment above it), the construction of the second plan uses a graph that is too large, causing a failure in some cases.

This fails in the following combination of conditions:

1. a project with squashed migrations in more than one app
2. where the squashed migrations do not have follow-up migrations (i.e. when the graph is not simplified for replacements, both the squashing migration and the last migration it replaces show up as leaf nodes)
3. explicit migration to a replaced migration from the command line.

because of the explicit migration to a replaced migration (3) the graph is updated in the ""wrong code"" as per the comment. Then, when trying to `_create_project_state` the graph contains leaf nodes for both the original and squashed paths, which means that some things in the project-state-creation happen doubly, which fails.

I have encountered this while working on [https://www.bugsink.com/ Bugsink, a self-hosted Error Tracker], on [https://github.com/bugsink/bugsink/tree/0b42d3ff1e0344a79bf3784c7cf2d68ea0d20a29 this commit]. I have not been able to distill a more clean PoC that actually exhibits failure though.

Replacing the mutation with something that leaves the loader untouched (ugly version below) fixes the problem though.

{{{
                    old_loader = self.loader
                    self.loader = MigrationLoader(self.connection)
                    self.loader.replace_migrations = False
                    self.loader.build_graph()
                    result = self.migration_plan(targets, clean_start=clean_start)
                    self.loader = old_loader
                    return result
}}}

[https://code.djangoproject.com/ticket/36166#ticket this ticket may or may not be related]"	Bug	assigned	Migrations	5.1	Normal			Jacob Walls	Accepted	1	0	0	1	0	0
