Opened 3 weeks ago

Closed 2 weeks ago

Last modified 2 weeks ago

#37431 closed Bug (fixed)

MigrationWriter silently serializes Python 3.15 frozendict values as tuples of keys

Reported by: amanjain Owned by: amanjain
Component: Migrations Version: 6.1
Severity: Release blocker Keywords:
Cc: 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 (last modified by amanjain)

On 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.

Reproduced against Django main at d430084791.

Minimal reproducer:

from django.db.migrations.writer import MigrationWriter

MigrationWriter.serialize(frozendict(answer=42))

Actual result:

("('answer',)", set())

Expected behavior: preserve the mapping's keys and values instead of serializing it as a tuple of keys.

This affects field defaults:

models.JSONField(default=frozendict(answer=42))

The generated migration contains:

models.JSONField(default=('answer',))

The database consequence was reproduced on SQLite: executing a generated AddField migration against an existing row stores ["answer"] instead of {"answer": 42}.

The serializer registry handles dict explicitly, but frozendict is not a dict subclass and falls through to IterableSerializer.

A 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+.

Validation:

  • Python 3.15: 75 writer/regression tests passed.
  • Python 3.15: 181 migration operation tests ran successfully, with 2 skipped.
  • Python 3.14: 75 writer/regression tests ran successfully, with 3 skipped.
  • Formatting and whitespace checks passed.
  • The full Django test suite has not been run.

AI disclosure: OpenAI Codex (GPT-6) assisted with investigation, implementation, regression tests, documentation, and commit preparation.

Change History (8)

comment:1 by amanjain, 3 weeks ago

Component: Uncategorized → Migrations
Description: modified (diff)
Version: 6.1 → dev

comment:2 by amanjain, 3 weeks ago

Proposed patch: ​https://github.com/django/django/pull/22157

Regression tests reproduce both the incorrect serialization and the incorrect JSON stored for existing rows.

comment:3 by amanjain, 3 weeks ago

Has patch: set

comment:4 by Jacob Walls, 3 weeks ago

Owner: set to amanjain
Patch needs improvement: set
Severity: Normal → Release blocker
Status: new → assigned
Triage Stage: Unreviewed → Accepted
Type: Uncategorized → Bug

Thanks, I see we need similar special handling just like frozenset. We'll backport this to 6.1 as part of 3.15 support. You don't need a release note, as we caught it before the first release declaring support for 3.15.

comment:5 by Jacob Walls, 3 weeks ago

Version: dev → 6.1

comment:6 by Jacob Walls, 2 weeks ago

Patch needs improvement: unset
Triage Stage: Accepted → Ready for checkin

comment:7 by Jacob Walls <jacobtylerwalls@…>, 2 weeks ago

Resolution: → fixed
Status: assigned → closed

In 2fff3e9f:

Fixed #37431 -- Added support for serializing frozendicts in migrations.

comment:8 by Jacob Walls <jacobtylerwalls@…>, 2 weeks ago

In 9f52f9a:

[6.1.x] Fixed #37431 -- Added support for serializing frozendicts in migrations.

Backport of 2fff3e9f493a9c85fb8a2294358c6751575ee4bf from main.

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