﻿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
36545	On Postgres, removing a generated field and its dependent field generates an invalid migration with makemigrations	john-parton		"When running 'makemigrations' to remove a GeneratedField and a field that it depends, produces a migration that cannot run.

Consider the following example model:

{{{
class ProductImage(models.Model):
    type = models.TextField()
    is_dupe = models.BooleanField(editable=False, null=True, default=None)

    visible = models.GeneratedField(
        expression=(
            Case(
                When(
                    Q(type=""hidden"") | Q(is_dupe=True),
                    then=Value(False),
                ),
                default=Value(True), 
            )
        ),
        output_field=models.BooleanField(),
        db_persist=True,
    )
}}}

Removing the `is_dupe` and `visible` fields produces the expected migration:


{{{
from django.db import migrations


class Migration(migrations.Migration):
    dependencies = [
        (""catalog"", ""0259_alter_productimage_options""),
    ]

    operations = [
        migrations.RemoveField(
            model_name=""productimage"",
            name=""is_dupe"",
        ),
        migrations.RemoveField(
            model_name=""productimage"",
            name=""visible"",
        ),
    ]
}}}

Running the migration produces this error

{{{
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/utils.py"", line 103, in _execute
    return self.cursor.execute(sql)
           ~~~~~~~~~~~~~~~~~~~^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/psycopg/cursor.py"", line 97, in execute
    raise ex.with_traceback(None)
psycopg.errors.UndefinedColumn: column ""visible"" of relation ""catalog_productimage"" does not exist

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File ""/home/john/Code/ecom/code/src/./manage.py"", line 30, in <module>
    execute_from_command_line(sys.argv)
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/core/management/__init__.py"", line 442, in execute_from_command_line
    utility.execute()
    ~~~~~~~~~~~~~~~^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/core/management/__init__.py"", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/core/management/base.py"", line 416, in run_from_argv
    self.execute(*args, **cmd_options)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/core/management/base.py"", line 460, in execute
    output = self.handle(*args, **options)
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/core/management/base.py"", line 107, in wrapper
    res = handle_func(*args, **kwargs)
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/core/management/commands/migrate.py"", line 353, in handle
    post_migrate_state = executor.migrate(
        targets,
    ...<3 lines>...
        fake_initial=fake_initial,
    )
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/migrations/executor.py"", line 135, in migrate
    state = self._migrate_all_forwards(
        state, plan, full_plan, fake=fake, fake_initial=fake_initial
    )
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/migrations/executor.py"", line 167, in _migrate_all_forwards
    state = self.apply_migration(
        state, migration, fake=fake, fake_initial=fake_initial
    )
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/migrations/executor.py"", line 255, in apply_migration
    state = migration.apply(state, schema_editor)
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/migrations/migration.py"", line 132, in apply
    operation.database_forwards(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        self.app_label, schema_editor, old_state, project_state
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/migrations/operations/fields.py"", line 174, in database_forwards
    schema_editor.remove_field(
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^
        from_model, from_model._meta.get_field(self.name)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/base/schema.py"", line 829, in remove_field
    self.execute(sql)
    ~~~~~~~~~~~~^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/pgtrigger/migrations.py"", line 483, in execute
    return super().execute(*args, **kwargs)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/postgresql/schema.py"", line 48, in execute
    return super().execute(sql, None)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/base/schema.py"", line 204, in execute
    cursor.execute(sql, params)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/utils.py"", line 122, in execute
    return super().execute(sql, params)
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/utils.py"", line 79, in execute
    return self._execute_with_wrappers(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        sql, params, many=False, executor=self._execute
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/utils.py"", line 92, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/utils.py"", line 100, in _execute
    with self.db.wrap_database_errors:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/utils.py"", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/django/db/backends/utils.py"", line 103, in _execute
    return self.cursor.execute(sql)
           ~~~~~~~~~~~~~~~~~~~^^^^^
  File ""/home/john/Code/ecom/.venv/lib/python3.13/site-packages/psycopg/cursor.py"", line 97, in execute
    raise ex.with_traceback(None)
django.db.utils.ProgrammingError: column ""visible"" of relation ""catalog_productimage"" does not exist
}}}

Manually reordering the migration so that generated field is deleted before the dependent field:

{{{
class Migration(migrations.Migration):
    dependencies = [
        (""catalog"", ""0259_alter_productimage_options""),
    ]

    operations = [
        migrations.RemoveField(
            model_name=""productimage"",
            name=""visible"",
        ),
        migrations.RemoveField(
            model_name=""productimage"",
            name=""is_dupe"",
        ),
    ]
}}}

And now the migration runs as expected.

There might be some other subtle errors around GeneratedField and migrations,  but this is the most obvious one I could reproduce.

Could not find a duplicate: https://code.djangoproject.com/query?description=~generated+field+migration&status=assigned&status=closed&status=new&order=id&desc=1&col=id&col=summary&col=owner&col=type&col=component"	Bug	new	Migrations	5.2	Normal				Unreviewed	0	0	0	0	0	0
