﻿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
35595	Generated migration for removing related models can't migrate backwards	Tim Schilling	wookkl	"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.

1. `python manage.py makemigrations [app]` -> 0001_initial.py (in my test it was the second migration, in case you can't reproduce it)
2. Remove both models
3. `python manage.py makemigrations [app]` -> 0002_remove.py
4. `python 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:

1. 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"",
),
}}}

2. Removing the `RemoveField` operation from the removal migration and rearranging the models.


{{{
operations = [
    migrations.DeleteModel(
        name=""Dog"",
    ),
    migrations.DeleteModel(
        name=""Animal"",
    ),
]
}}}
"	Bug	closed	Migrations	5.0	Normal	fixed			Ready for checkin	1	0	0	0	0	0
