#35422 closed Bug (fixed)
Migration crash when renaming a field referenced in GeneratedField.expression
| Reported by: | Sarah Boyce | Owned by: | Mariusz Felisiak |
|---|---|---|---|
| Component: | Migrations | Version: | 5.0 |
| Severity: | Release blocker | Keywords: | |
| Cc: | Simon Charette, Mariusz Felisiak, Lily Foote, David Sanders, Bhuvnesh | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Given a model (migrated) on SQLite
class Foo(models.Model):
name = models.CharField(max_length=10)
lower_name = models.GeneratedField(
expression=Lower("name"),
output_field=models.CharField(max_length=10),
db_persist=True,
)
when you update to
class Foo(models.Model):
surname = models.CharField(max_length=10)
lower_name = models.GeneratedField(
expression=Lower("surname"),
output_field=models.CharField(max_length=10),
db_persist=True,
)
and makemigrations, you then get asked Was foo.name renamed to foo.surname (a CharField)? [y/N] and say y (because it was)
You then get a crash when migrating:
Operations to perform:
Apply all migrations: app3
Running migrations:
Applying app3.0005_rename_name_foo_surname_alter_foo_lower_name...Traceback (most recent call last):
File "path_to_project\mysite\manage.py", line 22, in <module>
main()
File "path_to_project\mysite\manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "path_to_project\venv\Lib\site-packages\django\core\management\__init__.py", line 442, in execute_from_command_line
utility.execute()
File "path_to_project\venv\Lib\site-packages\django\core\management\__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "path_to_project\venv\Lib\site-packages\django\core\management\base.py", line 413, in run_from_argv
self.execute(*args, **cmd_options)
File "path_to_project\venv\Lib\site-packages\django\core\management\base.py", line 459, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\core\management\base.py", line 107, in wrapper
res = handle_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\core\management\commands\migrate.py", line 356, in handle
post_migrate_state = executor.migrate(
^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\migrations\executor.py", line 135, in migrate
state = self._migrate_all_forwards(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\migrations\executor.py", line 167, in _migrate_all_forwards
state = self.apply_migration(
^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\migrations\executor.py", line 255, in apply_migration
state = migration.apply(state, schema_editor)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\migrations\migration.py", line 132, in apply
operation.database_forwards(
File "path_to_project\venv\Lib\site-packages\django\db\migrations\operations\fields.py", line 241, in database_forwards
schema_editor.alter_field(from_model, from_field, to_field)
File "path_to_project\venv\Lib\site-packages\django\db\backends\base\schema.py", line 875, in alter_field
or old_field.generated_sql(self.connection)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\models\fields\generated.py", line 58, in generated_sql
resolved_expression = self.expression.resolve_expression(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\models\expressions.py", line 1052, in resolve_expression
c.source_expressions[pos] = arg.resolve_expression(
^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\models\expressions.py", line 874, in resolve_expression
return query.resolve_ref(self.name, allow_joins, reuse, summarize)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\models\sql\query.py", line 2010, in resolve_ref
join_info = self.setup_joins(
^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\models\sql\query.py", line 1863, in setup_joins
path, final_field, targets, rest = self.names_to_path(
^^^^^^^^^^^^^^^^^^^
File "path_to_project\venv\Lib\site-packages\django\db\models\sql\query.py", line 1768, in names_to_path
raise FieldError(
django.core.exceptions.FieldError: Cannot resolve keyword 'name' into field. Choices are: id, lower_name, surname
Change History (11)
comment:1 by , 18 months ago
| Cc: | added |
|---|---|
| Severity: | Normal → Release blocker |
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 18 months ago
| Cc: | added |
|---|
comment:3 by , 18 months ago
I don't think the problem is specific to SQLite.
The non-invasive back portable solution I can think of is for FieldError to be caught and also result in a ValueError pointing out that the generated field must be removed first.
In other words, if we can't generate the SQL for the old field anymore but we can for the new one then it was certainly changed and the same lack of alteration support limitation applies.
comment:4 by , 18 months ago
| Summary: | Migration crash when renaming a field referenced in GeneratedField.expression on SQLite → Migration crash when renaming a field referenced in GeneratedField.expression |
|---|
In other words, if we can't generate the SQL for the old field anymore but we can for the new one then it was certainly changed and the same lack of alteration support limitation applies.
Makes sense! Agree that I found this on SQLite is irrelevant
comment:5 by , 18 months ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:7 by , 18 months ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
Thank you Sarah, I reproduced and confirmed that this is not a dupe of #35359 (or at least the fix for that ticket is not fixing this issue).
This is a release blocker for 5.0 given that
GeneratedFieldwas added in that Django version. I also reproduced in f333e3513e8bdf5ffeb6eeb63021c230082e6f95.