[Django] #36545: On Postgres, removing a generated field and its dependent field generates an invalid migration with makemigrations

12 views
Skip to first unread message

Django

unread,
Aug 8, 2025, 12:45:25 PMAug 8
to django-...@googlegroups.com
#36545: On Postgres, removing a generated field and its dependent field generates
an invalid migration with makemigrations
-----------------------------+--------------------------------------
Reporter: john-parton | Type: Bug
Status: new | Component: Migrations
Version: 5.2 | Severity: Normal
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------
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.
--
Ticket URL: <https://code.djangoproject.com/ticket/36545>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Aug 8, 2025, 12:46:35 PMAug 8
to django-...@googlegroups.com
#36545: On Postgres, removing a generated field and its dependent field generates
an invalid migration with makemigrations
-----------------------------+--------------------------------------
Reporter: john-parton | Owner: (none)
Type: Bug | Status: new
Component: Migrations | Version: 5.2
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------
Description changed by john-parton:

Old description:
New description:
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

--
--
Ticket URL: <https://code.djangoproject.com/ticket/36545#comment:1>

Django

unread,
Aug 8, 2025, 12:47:38 PMAug 8
to django-...@googlegroups.com
New description:

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 a migration that
looks reasonable at first:
Ticket URL: <https://code.djangoproject.com/ticket/36545#comment:2>

Django

unread,
Aug 9, 2025, 10:58:39 AMAug 9
to django-...@googlegroups.com
#36545: On Postgres, removing a generated field and its dependent field generates
an invalid migration with makemigrations
-----------------------------+--------------------------------------
Reporter: john-parton | Owner: (none)
Type: Bug | Status: closed
Component: Migrations | Version: 5.2
Severity: Normal | Resolution: duplicate
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------
Changes (by Clifford Gama):

* resolution: => duplicate
* status: new => closed

Comment:

I believe this is a duplicate of #36438
--
Ticket URL: <https://code.djangoproject.com/ticket/36545#comment:3>

Django

unread,
Aug 11, 2025, 4:46:11 PMAug 11
to django-...@googlegroups.com
#36545: On Postgres, removing a generated field and its dependent field generates
an invalid migration with makemigrations
-----------------------------+--------------------------------------
Reporter: john-parton | Owner: (none)
Type: Bug | Status: closed
Component: Migrations | Version: 5.2
Severity: Normal | Resolution: duplicate
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-----------------------------+--------------------------------------
Comment (by john-parton):

That does look right. Thanks for finding that. I couldn't guess the right
keywords.
--
Ticket URL: <https://code.djangoproject.com/ticket/36545#comment:4>
Reply all
Reply to author
Forward
0 new messages