• 
      

    Make MoveToDjangoMigrations work when the app isn't yet in the database.

    Review Request #14530 — Created July 30, 2025 and submitted

    Information

    Django Evolution
    release-2.x

    Reviewers

    MoveToDjangoMigrations had a dependency on the migrations being
    applied. The idea was that:

    1. If this was a new app in the database, we'd want migrations to handle
      any initial work first.

    2. 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 like after_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 a 0001_initial over time.

    There are also fixes for the upgrade tests, which had previously been a
    no-op due to a leftover return, masking some bad test harness setup.

    Tested on all versions of Django, on all supported databases.

    Summary ID
    Make MoveToDjangoMigrations work when the app isn't yet in the database.
    `MoveToDjangoMigrations` had a dependency on the migrations being applied. The idea was that: 1. If this was a new app in the database, we'd want migrations to handle any initial work first. 2. 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 like `after_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 a `0001_initial` over time. There are also fixes for the upgrade tests, which had previously been a no-op due to a leftover `return`, masking some bad test harness setup.
    939ebccfd589efd2a9c8ba3028d390b984ff7a7f
    Description From Last Updated

    line too long (86 > 79 characters) Column: 80 Error code: E501

    reviewbotreviewbot
    Checks run (1 failed, 1 succeeded)
    flake8 failed.
    JSHint passed.

    flake8

    david
    1. Ship It!
    2. 
        
    chipx86
    Review request changed
    Status:
    Completed
    Change Summary:
    Pushed to release-2.x (eb70663)