Changes between Initial Version and Version 1 of Ticket #37431


Ignore:
Timestamp:
Oct 15, 2026, 9:07:50 PM (95 minutes ago)
Author:
amanjain
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #37431

    • Property Component Uncategorized → Migrations
    • Property Version 6.1 → dev
  • Ticket #37431 – Description

    initial v1  
    1 **Ticket title:**
    2 
    3 ```
    4 MigrationWriter silently serializes Python 3.15 frozendict values as tuples of keys
    5 ```
    6 
    7 **Ticket description:**
    8 
    9 ````
    101On Python 3.15.0, Django's migration serializer treats built-in frozendict instances as generic iterables, preserving only their keys and silently discarding their values.
    112
    … …  
    145Minimal reproducer:
    156
    16 ```python
     7{{{#!python
    178from django.db.migrations.writer import MigrationWriter
    189
    1910MigrationWriter.serialize(frozendict(answer=42))
    20 ```
     11}}}
    2112
    2213Actual result:
    2314
    24 ```python
     15{{{#!python
    2516("('answer',)", set())
    26 ```
     17}}}
    2718
    28 Expected behavior: preserve the mapping's keys and values, rather than serialize it as a tuple of keys.
     19Expected behavior: preserve the mapping's keys and values instead of serializing it as a tuple of keys.
    2920
    30 This affects field defaults. For example:
     21This affects field defaults:
    3122
    32 ```python
     23{{{#!python
    3324models.JSONField(default=frozendict(answer=42))
    34 ```
     25}}}
    3526
    36 is written into a migration as:
     27The generated migration contains:
    3728
    38 ```python
     29{{{#!python
    3930models.JSONField(default=('answer',))
    40 ```
     31}}}
    4132
    42 I reproduced the database consequence on SQLite: after executing a generated AddField migration against a table containing an existing row, the new JSON field contains ["answer"] instead of {"answer": 42}.
     33The database consequence was reproduced on SQLite: executing a generated AddField migration against an existing row stores {{{["answer"]}}} instead of {{{{"answer": 42}}}}.
    4334
    4435The serializer registry handles dict explicitly, but frozendict is not a dict subclass and falls through to IterableSerializer.
    4536
    46 A proposed patch adds a dedicated frozendict serializer, regression tests in the existing migration writer and operation suites, and documentation noting that generated migrations containing frozendict require Python 3.15+.
     37A proposed patch adds a dedicated serializer, regression tests in the migration writer and operation suites, and documentation noting that generated migrations containing frozendict require Python 3.15+.
    4738
    4839Validation:
    49 - Python 3.15: all 75 writer/regression tests passed.
    50 - Python 3.15: 181 migration operation tests ran successfully, with 2 skipped.
    51 - Python 3.14: 75 writer/regression tests ran successfully, with 3 skipped.
    52 - Formatting and whitespace checks passed.
    53 - The full Django test suite has not been run.
     40 * Python 3.15: 75 writer/regression tests passed.
     41 * Python 3.15: 181 migration operation tests ran successfully, with 2 skipped.
     42 * Python 3.14: 75 writer/regression tests ran successfully, with 3 skipped.
     43 * Formatting and whitespace checks passed.
     44 * The full Django test suite has not been run.
    5445
    5546AI disclosure: OpenAI Codex (GPT-6) assisted with investigation, implementation, regression tests, documentation, and commit preparation.
    56 ````
Back to Top