Make MoveToDjangoMigrations work when the app isn't yet in the database.
Review Request #14530 — Created July 30, 2025 and submitted — Latest diff uploaded
MoveToDjangoMigrations
had a dependency on the migrations being
applied. The idea was that:
If this was a new app in the database, we'd want migrations to handle
any initial work first.If the app was already installed, but the evolution was new, those
migrations would have already been applied.The problem was that the first case didn't necessarily work right.
We could reach a state where the migrations weren't in the graph, and
the dependency would break. This could happen if installing an app that
has both migrations and evolutions defined as custom for the app.
There's no signature for the app yet, and we have evolutions, so we
prioritize those and skip the migrations (so we can deal with any
potentially-broken state in the migratinos).In this case, there was no way of really communicating that the
migrations should have been in the graph but also should not have been
applied since the evolution replaces them.This change adds that capability. Along with the before/after
migrations/evolutions dependencies, there's now a new
replace_migrations
dependency type that works likeafter_migrations
but makes the dependencies optional. This allows them to depend on a
recorded migration if it's in the graph as before, but to proceed
without failure if that migration wasn't found in the graph.With that, we can keep a consistent graph in the case where the
migration would have been processed or picked up, but without requiring
it to be applied or recorded first in this case.The new test app (
move_to_migrations_app
) simulates a module that
(in cooperation with the test setup) builds upon a faulty base (a
0001_initial
that contains a superset of the fields actually in the
base model), adds missing fields in the evolution, hands it off to
Django, and then adds a field via a migration. This simulates the kind
of problems we've seen in some third-party apps that squash history back
down into a0001_initial
over time.There are also fixes for the upgrade tests, which had previously been a
no-op due to a leftoverreturn
, masking some bad test harness setup.
Tested on all versions of Django, on all supported databases.