Opened 9 days ago

Last modified 8 days ago

#36166 new Bug

Backwards migration to replaced migration leads to InconsistentMigrationHistory on forward migrate

Reported by: Klaas van Schelven Owned by:
Component: Migrations Version: 5.1
Severity: Normal Keywords:
Cc: Jacob Walls Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When squashing migrations, migrating back is broken when referencing a now-replaced migration.

I have migrations as such:

  • 0001_initial.py
  • 0002_foomodel_bar.py
  • 0001_squashed_0002_foomodel_bar.py squashing 0001 & 0002
  • 0003_foomodel_after_squash.py depending on the squashed migration (Django does this by default when making a new migration after squashing, and it's the right thing)

However, after migrating fully, migrating back to the last replaced migration does not actually undo 0003.

$ python manage.py migrate squashme
Operations to perform:
  Apply all migrations: squashme
Running migrations:
  Applying squashme.0001_squashed_0002_foomodel_bar... OK
  Applying squashme.0003_foomodel_after_squash... OK

$ python manage.py migrate squashme 0002
Operations to perform:
  Target specific migration: 0002_foomodel_bar, from squashme
Running migrations:
  No migrations to apply.
(freshdjango) klaas@pop-os:~/dev/squashtest$ python manage.py migrate squashme
Operations to perform:
  Apply all migrations: squashme
Running migrations:
  No migrations to apply.

Migrating even further back into that chain leads to inconsistent migration history:

(freshdjango) python manage.py migrate squashme
Operations to perform:
  Apply all migrations: squashme
Running migrations:
  Applying squashme.0001_squashed_0002_foomodel_bar... OK
  Applying squashme.0003_foomodel_after_squash... OK

(freshdjango) klaas@pop-os:~/dev/squashtest$ python manage.py migrate squashme 0001_initial
Operations to perform:
  Target specific migration: 0001_initial, from squashme
Running migrations:
  Rendering model states... DONE
                                                                <= shouldn't there be undoing of 0003?
  Unapplying squashme.0002_foomodel_bar... OK

(freshdjango) klaas@pop-os:~/dev/squashtest$ python manage.py migrate squashme
Traceback (most recent call last):
[..]
    raise InconsistentMigrationHistory(
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration squashme.0003_foomodel_after_squash is applied before its dependency squashme.0002_foomodel_bar on database 'default'.

Migrating back to the squashing migration instead (if you didn't do the above) does what you'd expect:

(freshdjango)  python manage.py migrate squashme 0001_squashed_0002_foomodel_bar
Operations to perform:
  Target specific migration: 0001_squashed_0002_foomodel_bar, from squashme
Running migrations:
  Rendering model states... DONE
  Unapplying squashme.0003_foomodel_after_squash... OK

code sample here

Change History (3)

comment:1 by Sarah Boyce, 8 days ago

Summary: squashmigrations breaks backward migration detectionBackwards migration to replaced migration leads to InconsistentMigrationHistory on forward migrate
Triage Stage: UnreviewedAccepted

Note that if you were to recreate the squashed migration, it would be named 0001_initial_squashed_0002_foomodel_bar.py but it appears the file and dependency was renamed to 0001_squashed_0002_foomodel_bar.py.

When it had the original name, we do not hit the InconsistentMigrationHistory error because doing python manage.py migrate squashme 0001_initial gives:
CommandError: More than one migration matches '0001_initial' in app 'squashme'. Please be more specific.

Note that doing python manage.py migrate squashme zero and then migrate still un-applies and applies the migration correctly (this is also true with the renamed migration)

I'm not sure it makes sense to migrate backwards to replaced migrations (and we could error instead of allowing it)
Replicated on 5.1 and main. Thank you for the ticket and project 👍

Possibly related #24900

comment:2 by Klaas van Schelven, 8 days ago

it would be named 0001_initial_squashed_0002_foomodel_bar.py

Not in my world (Django 5.1):

(freshdjango) $ python manage.py squashmigrations squashme 0002
Will squash the following migrations:
 - 0001_initial
 - 0002_foomodel_bar
Do you wish to proceed? [y/N] y
Optimizing...
  Optimized from 2 operations to 1 operations.
Created new squashed migration /mnt/datacrypt/dev/squashtest/squashme/migrations/0001_squashed_0002_foomodel_bar.py
  You should commit this migration but leave the old ones in place;
  the new migration will be used for new installs. Once you are sure
  all instances of the codebase have applied the migrations you squashed,
  you can delete them.

When it had the original name

I'm not sure that line of reasoning matters much to the present discussion, it's more of a separate annoyance that makes it inconvenient (impossible even) to specify a specific migration when the full name of one is the prefix of another... :-D

I'm not sure it makes sense to migrate backwards to replaced migrations (and we could error instead of allowing it)

I'm not sure either... but:

Possibly related: #24900

"Allowed migrating backward to squashed migrations" is the name of the PR, so it seems this is indeed the ambition

Last edited 8 days ago by Klaas van Schelven (previous) (diff)

comment:3 by Sarah Boyce, 8 days ago

Cc: Jacob Walls added

Not in my world (Django 5.1):

Ah apologies, I had ran python manage.py squashmigrations squashme 0001 0002 which had the different name. Replicated

I'm not sure that line of reasoning matters much to the present discussion

I was thinking that there were additional manual steps here (which I believe would be relevant as it would reduce the likelihood of someone encountering the error), but the commands being ran to create the migration were different.

cc-ing Jacob as he was involved in that PR

Note the commentary of #36168 might be useful to this ticket

Last edited 8 days ago by Sarah Boyce (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top