Opened 23 months ago

Closed 22 months ago

Last modified 22 months ago

#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 Mariusz Felisiak, 23 months ago

Component: Database layer (models, ORM)Migrations
Triage Stage: UnreviewedAccepted

Thanks for the report.

comment:2 by Bhuvnesh, 23 months ago

Owner: changed from nobody to Bhuvnesh
Status: newassigned

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 Zac Miller, 23 months 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 Bhuvnesh, 23 months 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 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!

Version 0, edited 23 months ago by Bhuvnesh (next)

comment:5 by Bhuvnesh, 22 months ago

Needs tests: set

comment:6 by Bhuvnesh, 22 months ago

Has patch: set
Needs tests: unset
Last edited 22 months ago by Bhuvnesh (previous) (diff)

comment:7 by Bhuvnesh, 22 months ago

Has patch: unset

comment:8 by Bhuvnesh, 22 months ago

Has patch: set

comment:9 by Bhuvnesh, 22 months ago

Hi, the PR is ready for review !

comment:10 by Mariusz Felisiak, 22 months ago

Cc: Markus Holtermann Simon Charette added

comment:11 by Mariusz Felisiak, 22 months ago

Patch needs improvement: set

comment:12 by Simon Charette, 22 months ago

Patch needs improvement: unset

Patch is looking good to me. If the signature change to accommodate for alter_field(force) are judged too 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.

Last edited 22 months ago by Tim Graham (previous) (diff)

comment:13 by Bhuvnesh, 22 months ago

Made changes to use private _alter_many_to_many as per Mariusz's comment

comment:14 by Mariusz Felisiak, 22 months ago

Triage Stage: AcceptedReady for checkin

comment:15 by Mariusz Felisiak <felisiak.mariusz@…>, 22 months ago

Resolution: fixed
Status: assignedclosed

In ff3a283:

Fixed #34250 -- Fixed renaming model with m2m relation to a model with the same name.

comment:16 by Mariusz Felisiak <felisiak.mariusz@…>, 22 months ago

In 5cde08f7:

[4.2.x] Fixed #34250 -- Fixed renaming model with m2m relation to a model with the same name.

Backport of ff3a2834224f527ca574b5cd0d578c8c26d51a6c from main

Note: See TracTickets for help on using tickets.
Back to Top