Opened 94 minutes ago

Last modified 6 minutes ago

#36878 assigned Bug

Migration's ModelState has varying type for unique_together and index_together options causing autodetector crash

Reported by: Markus Holtermann Owned by: Markus Holtermann
Component: Migrations Version: dev
Severity: Normal Keywords:
Cc: Lily Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

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):

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 (see the ModelState.from_model() method).

Unfortunately, there are situations inside the ProjectState's mutation functions (e.g. rename_field()) where the data type for model_state.options["unique_together"] is changed to list[list[str]]:

        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]
                ]

Change History (1)

comment:1 by Simon Charette, 6 minutes ago

Triage Stage: UnreviewedAccepted
Type: UncategorizedBug
Note: See TracTickets for help on using tickets.
Back to Top