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'
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34250>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* component: Database layer (models, ORM) => Migrations
* stage: Unreviewed => Accepted
Comment:
Thanks for the report.
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:1>
* owner: nobody => Bhuvnesh
* status: new => assigned
Comment:
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?
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:2>
Comment (by Zac Miller):
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.
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:3>
Comment (by Bhuvnesh):
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
[https://github.com/django/django/blob/main/django/db/models/fields/related.py#L1258
here.]
One proposed solution 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!
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:4>
* needs_tests: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:5>
* has_patch: 0 => 1
* needs_tests: 1 => 0
Comment:
[https://github.com/django/django/pull/16532 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:6>
* has_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:7>
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:8>
Comment (by Bhuvnesh):
Hi, the PR is ready for review !
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:9>
* cc: Markus Holtermann, Simon Charette (added)
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:10>
* needs_better_patch: 0 => 1
Comment:
Per
[https://github.com/django/django/pull/16532#pullrequestreview-1292487861
Simon's comment].
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:11>
* needs_better_patch: 1 => 0
Comment:
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.
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:12>
Comment (by Bhuvnesh):
Made changes to use private {{{_alter_many_to_many}}} as per
[https://github.com/django/django/pull/16532#issuecomment-1427591547
Mariusz's comment]
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:13>
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:14>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"ff3a2834224f527ca574b5cd0d578c8c26d51a6c" ff3a283]:
{{{
#!CommitTicketReference repository=""
revision="ff3a2834224f527ca574b5cd0d578c8c26d51a6c"
Fixed #34250 -- Fixed renaming model with m2m relation to a model with the
same name.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:15>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"5cde08f702c1db7646c0c1c8f9d72d01067310df" 5cde08f7]:
{{{
#!CommitTicketReference repository=""
revision="5cde08f702c1db7646c0c1c8f9d72d01067310df"
[4.2.x] Fixed #34250 -- Fixed renaming model with m2m relation to a model
with the same name.
Backport of ff3a2834224f527ca574b5cd0d578c8c26d51a6c from main
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34250#comment:16>