#36878: Migration's ModelState has varying type for unique_together and
index_together options causing autodetector crash
-------------------------------------+-------------------------------------
               Reporter:  Markus     |          Owner:  Markus Holtermann
  Holtermann                         |
                   Type:             |         Status:  assigned
  Uncategorized                      |
              Component:             |        Version:  dev
  Migrations                         |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 When leveraging the `MigrationAutodetector` to get the changes between two
 project states, the following exception is raised (on 5.2, but the same
 applies to the current master at commit
 b1ffa9a9d78b0c2c5ad6ed5a1d84e380d5cfd010):

 {{{#!python
 Traceback (most recent call last):
   File "<frozen runpy>", line 198, in _run_module_as_main
   File "<frozen runpy>", line 88, in _run_code
   File "src/manage.py", line 67, in <module>
     management.execute_from_command_line(sys.argv)
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
   File ".venv/lib/python3.13/site-
 packages/django/core/management/__init__.py", line 442, in
 execute_from_command_line
     utility.execute()
     ~~~~~~~~~~~~~~~^^
   File ".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 ".venv/lib/python3.13/site-
 packages/django/core/management/base.py", line 420, in run_from_argv
     self.execute(*args, **cmd_options)
     ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
   File ".venv/lib/python3.13/site-
 packages/django/core/management/base.py", line 464, in execute
     output = self.handle(*args, **options)
   File "src/core/management/commands/some_command.py", line 94, in handle
     self._get_migrations_and_operations(section)
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
   File "src/core/management/commands/some_command.py", line 238, in
 _get_migrations_and_operations
     new_migrations = autodetector.changes(self.graph,
 trim_to_apps={"affiliates"})
   File ".venv/lib/python3.13/site-
 packages/django/db/migrations/autodetector.py", line 67, in changes
     changes = self._detect_changes(convert_apps, graph)
   File ".venv/lib/python3.13/site-
 packages/django/db/migrations/autodetector.py", line 213, in
 _detect_changes
     self.generate_removed_altered_unique_together()
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
   File ".venv/lib/python3.13/site-
 packages/django/db/migrations/autodetector.py", line 1718, in
 generate_removed_altered_unique_together
 self._generate_removed_altered_foo_together(operations.AlterUniqueTogether)
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   File ".venv/lib/python3.13/site-
 packages/django/db/migrations/autodetector.py", line 1699, in
 _generate_removed_altered_foo_together
     ) in self._get_altered_foo_together_operations(operation.option_name):
          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
   File ".venv/lib/python3.13/site-
 packages/django/db/migrations/autodetector.py", line 1668, in
 _get_altered_foo_together_operations
     new_value = set(new_value) if new_value else set()
                 ~~~^^^^^^^^^^^
 TypeError: unhashable type: 'list'
 }}}

 Reason:

 The migration `ModelState` tracks the changes for all options
 (`order_with_respect_to`, `unique_together`, `indexes`, ...) in its
 `options` attribute, which is a simple dict. For most keys inside the
 dict, the value is just a `list` of something. However, for
 `unique_together` and `index_together`, the value should be a set of
 tuples
 
([https://github.com/django/django/blob/b1ffa9a9d78b0c2c5ad6ed5a1d84e380d5cfd010/django/db/migrations/state.py#L841
 see the ModelState.from_model() method]).

 Unfortunately, there are situations inside the `ProjectState`'s mutation
 functions
 
([https://github.com/django/django/blob/b1ffa9a9d78b0c2c5ad6ed5a1d84e380d5cfd010/django/db/migrations/state.py#L340-L345
 e.g. rename_field()]) where the data type for
 `model_state.options["unique_together"]` is changed to `list[list[str]]`:

 {{{#!python
         for option in ("index_together", "unique_together"):
             if option in options:
                 options[option] = [
                     [new_name if n == old_name else n for n in together]
                     for together in options[option]
                 ]
 }}}
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36878>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/0107019beb00db91-e1fd9cbe-07f7-4474-ae02-791be2a33ee0-000000%40eu-central-1.amazonses.com.

Reply via email to