Opened 3 months ago
Last modified 3 months ago
#35595 assigned Bug
Generated migration for removing related models can't migrate backwards
Reported by: | Timothy Schilling | Owned by: | Akash Kumar Sen |
---|---|---|---|
Component: | Migrations | Version: | 5.0 |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
The migration that's created when removing two related models, results in a migration that can't be migrated backwards. However, this is only the case when the model with the foreign key has a Meta.indexes
specified that references the foreign key field.
Given the following models:
class Animal(models.Model): name = models.CharField(max_length=100) class Dog(models.Model): name = models.CharField(max_length=100) animal = models.ForeignKey(Animal, on_delete=models.CASCADE) class Meta: indexes = [models.Index(fields=("animal", "name"))]
Doing the following results in the error at the end.
python manage.py makemigrations [app]
-> 0001_initial.py (in my test it was the second migration, in case you can't reproduce it)- Remove both models
python manage.py makemigrations [app]
-> 0002_remove.pypython manage.py sqlmigrate [app] 0002 --backwards
Error:
❯ ./manage.py sqlmigrate endpoint 0003 --backwards Traceback (most recent call last): File "site-packages/django/db/models/options.py", line 681, in get_field return self.fields_map[field_name] ~~~~~~~~~~~~~~~^^^^^^^^^^^^ KeyError: 'animal' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/Users/schillingt/Projects/aspiredu/./manage.py", line 11, in <module> execute_from_command_line(sys.argv) File "site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line utility.execute() File "site-packages/django/core/management/__init__.py", line 436, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "site-packages/django/core/management/base.py", line 413, in run_from_argv self.execute(*args, **cmd_options) File "site-packages/django/core/management/commands/sqlmigrate.py", line 38, in execute return super().execute(*args, **options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "site-packages/django/core/management/base.py", line 459, in execute output = self.handle(*args, **options) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "site-packages/django/core/management/commands/sqlmigrate.py", line 80, in handle sql_statements = loader.collect_sql(plan) ^^^^^^^^^^^^^^^^^^^^^^^^ File "site-packages/django/db/migrations/loader.py", line 383, in collect_sql state = migration.unapply(state, schema_editor, collect_sql=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "site-packages/django/db/migrations/migration.py", line 193, in unapply operation.database_backwards( File "site-packages/django/db/migrations/operations/models.py", line 394, in database_backwards schema_editor.create_model(model) File "site-packages/django/db/backends/base/schema.py", line 513, in create_model self.deferred_sql.extend(self._model_indexes_sql(model)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "site-packages/django/db/backends/base/schema.py", line 1624, in _model_indexes_sql output.append(index.create_sql(model, self)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "site-packages/django/db/models/indexes.py", line 112, in create_sql model._meta.get_field(field_name) File "site-packages/django/db/models/options.py", line 683, in get_field raise FieldDoesNotExist( django.core.exceptions.FieldDoesNotExist: Dog has no field named 'animal'
The generated migrations for me are:
0001:
operations = [ migrations.CreateModel( name="Animal", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("name", models.CharField(max_length=100)), ], ), migrations.CreateModel( name="Dog", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("name", models.CharField(max_length=100)), ( "animal", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to="[app].animal", ), ), ], options={ "indexes": [ models.Index( fields=["animal", "name"], name="[app]_do_animal__b82a2f_idx" ) ], }, ), ]
0002:
operations = [ migrations.RemoveField( model_name="dog", name="animal", ), migrations.DeleteModel( name="Animal", ), migrations.DeleteModel( name="Dog", ), ]
My environment is:
- Django 5.0.6
- Postgres 15
- python 3.12.4
- M1 mac
Removing the Meta.indexes
results in a generated migration that can be migrated backwards.
This appears by solveable in at least two ways:
- Adding the following operation to the top of the removal migration solves the problem.
migrations.RemoveIndex( model_name="dog", name="[app]_do_animal__b82a2f_idx", ),
- Removing the
RemoveField
operation from the removal migration and rearranging the models.
operations = [ migrations.DeleteModel( name="Dog", ), migrations.DeleteModel( name="Animal", ), ]
Change History (2)
comment:1 by , 3 months ago
Triage Stage: | Unreviewed → Accepted |
---|---|
Type: | Uncategorized → Bug |
comment:2 by , 3 months ago
Owner: | set to |
---|---|
Status: | new → assigned |
Thank you for the report! Replicated on main 👍