#34250 closed Bug (fixed)
Duplicate model names in M2M relationship causes RenameModel migration failure
| Reported by: | Zac Miller | Owned by: | Bhuvnesh |
|---|---|---|---|
| Component: | Migrations | Version: | 4.1 |
| Severity: | Normal | Keywords: | RenameModel |
| Cc: | Markus Holtermann, Simon Charette | 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
Example code is here: https://github.com/jzmiller1/edemo
I have a django project with two apps, incidents and vault, that both have a model named Incident. The vault Incident model has an M2M involving the incidents Incident model. When the table is created for this M2M relationship the automatic field names are "from_incident_id" and "to_incident_id" since models have the same names.
If I then try to use a RenameModel in a migration...
operations = [
migrations.RenameModel(
old_name='Incident',
new_name='Folder',
),
]
it fails with this traceback:
Tracking file by folder pattern: migrations
Operations to perform:
Apply all migrations: admin, auth, contenttypes, incidents, sessions, vault
Running migrations:
Applying vault.0002_rename_incident_folder...Traceback (most recent call last):
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/models/options.py", line 668, in get_field
return self.fields_map[field_name]
KeyError: 'incident'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/zacmiller/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-0/222.4345.23/PyCharm.app/Contents/plugins/python/helpers/pycharm/django_manage.py", line 52, in <module>
run_command()
File "/Users/zacmiller/Library/Application Support/JetBrains/Toolbox/apps/PyCharm-P/ch-0/222.4345.23/PyCharm.app/Contents/plugins/python/helpers/pycharm/django_manage.py", line 46, in run_command
run_module(manage_file, None, '__main__', True)
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 209, in run_module
return _run_module_code(code, init_globals, run_name, mod_spec)
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 96, in _run_module_code
_run_code(code, mod_globals, init_globals,
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/runpy.py", line 86, in _run_code
exec(code, run_globals)
File "/Users/zacmiller/PycharmProjects/edemo/manage.py", line 22, in <module>
main()
File "/Users/zacmiller/PycharmProjects/edemo/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
utility.execute()
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/core/management/__init__.py", line 440, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/core/management/base.py", line 402, in run_from_argv
self.execute(*args, **cmd_options)
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/core/management/base.py", line 448, in execute
output = self.handle(*args, **options)
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/core/management/base.py", line 96, in wrapped
res = handle_func(*args, **kwargs)
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 349, in handle
post_migrate_state = executor.migrate(
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/migrations/executor.py", line 135, in migrate
state = self._migrate_all_forwards(
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
state = self.apply_migration(
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
state = migration.apply(state, schema_editor)
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/migrations/migration.py", line 130, in apply
operation.database_forwards(
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/migrations/operations/models.py", line 422, in database_forwards
old_m2m_model._meta.get_field(old_model._meta.model_name),
File "/Users/zacmiller/PycharmProjects/virtualenvs/edemo/lib/python3.10/site-packages/django/db/models/options.py", line 670, in get_field
raise FieldDoesNotExist(
django.core.exceptions.FieldDoesNotExist: Incident_incidents has no field named 'incident'
Change History (16)
comment:1 by , 3 years ago
| Component: | Database layer (models, ORM) → Migrations |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 3 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
Mariusz , if the names of models are same (but different apps), are there any expected names of the cloumns for m2m db table? Can it be incident_id and incidents_incident_id since two db columns cannot have same name?
comment:3 by , 3 years ago
I did some further testing and you don't encounter the same issue going in the other direction. For example, in the demo project if I rename incidents.Incident the migration generated works fine and properly updates the M2M on vault.Incident. The M2M table is updated and the from/to are removed from both. After that you can do a rename on the model with the M2M. Just an FYI for anyone who might be looking for a work around.
comment:4 by , 3 years ago
Thanks zac! I did some digging in the code and found out that even though the field names are from/to_incident , they are referring to the correct models:
CREATE TABLE "vault_incident" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(10) NOT NULL);
CREATE TABLE "vault_incident_incidents" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "from_incident_id" bigint NOT NULL REFERENCES "vault_incident" ("id") DEFERRABLE INITIALLY DEFERRED, "to_incident_id" bigint NOT NULL REFERENCE
S "incidents_incident" ("id") DEFERRABLE INITIALLY DEFERRED);
The problem is caused in renaming fields of m2m model here.
One of the solutions can be checking app names + model name together and if model names are same but apps are different the name of fields can be (like in above case) incident_id and incidents_incident_id.
Feel free to share if anyone has a better solution. Thanks!
comment:5 by , 3 years ago
| Needs tests: | set |
|---|
comment:6 by , 3 years ago
| Has patch: | set |
|---|---|
| Needs tests: | unset |
comment:7 by , 3 years ago
| Has patch: | unset |
|---|
comment:8 by , 3 years ago
| Has patch: | set |
|---|
comment:10 by , 3 years ago
| Cc: | added |
|---|
comment:12 by , 3 years ago
| Patch needs improvement: | unset |
|---|
Patch is looking good to me. If the signature change to accommodate for alter_field(force) are judged to invasive, they only have a single use after all, switching a call to the private _alter_many_to_many will have the same effect.
Thanks for the patch and tests Bhuvnesh.
comment:13 by , 3 years ago
Made changes to use private _alter_many_to_many as per Mariusz's comment
comment:14 by , 3 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
Thanks for the report.