[Django] #36892: Lazy Tuples in field Choices generate repeated migrations with no changes

1 view
Skip to first unread message

Django

unread,
Jan 29, 2026, 6:31:00 PM (8 hours ago) Jan 29
to django-...@googlegroups.com
#36892: Lazy Tuples in field Choices generate repeated migrations with no changes
-------------------------------------+-------------------------------------
Reporter: Matt Armand | Type: Bug
Status: new | Component:
| Migrations
Version: 5.0 | Severity: Normal
Keywords: migrations tuple | Triage Stage:
choices lazy functional | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
## Background

TLDR, when a model field specifies `choices` backed by a lazily evaluated
tuple, Django>=5.0 serializes them incorrectly into migrations files and
repeatedly generates identical migrations on repeated `makemigrations`
runs. Django 4.2 is able to handle these fields correctly.

This bug appears to me to have been introduced with the 5.0 release. At
time of writing, this bug exists in the latest release on the 5.2 channel
(5.2.10) as well as the 6.0 channel (6.0.1).

This issue was originally found in a Django application utilizing the a
list of US States in the `django-localflavor` library as the `choices` for
a model field. That library uses `django.utils.functional::lazy` for
creating a lazily evaluated tuple, and the bug is reproducible with pure
Django code and no external dependencies.

This is a minimalistic example of the lazy tuple used in `django-
localflavor` and that can be used to reproduce the bug:

{{{
import operator
from django.db import models
from django.utils.functional import lazy

TUPLE_1 = (("A", "A value"),)
TUPLE_2 = (("B", "B value"),)
LAZY_TUPLE = lazy(
lambda: tuple(sorted(TUPLE_1 + TUPLE_2, key =
operator.itemgetter(1))), tuple
)()

class TestModel(models.model):
test_field = models.CharField(choices=LAZY_TUPLE)
}}}

## Expected Behavior

Prior to Django 5.0 (in 4.2.27 for example), running `makemigrations` on
an app containing this field and model yields migration code containing
the following serialization: `choices=[('A', 'A value'), ('B', 'B
value')],` The choices attribute is an array as expected, and repeated
`makemigrations` calls successfully detect no changes to the model.

## Actual Behavior

Beginning in Django 5.0, running `makemigrations` on an app containing
this field and model yields migration code containing the following
serialization: `choices="(('A', 'A value'), ('B', 'B value'))",` The
choices attribute is now a string representation of the tuple, and
repeated `makemigrations` calls will re-generate a new and identical
`AlterField` migration for this field ad infinitum.

I've pushed a [https://github.com/matthewarmand/django-lazy-migration-bug
sample reproduction Django app]. You can see in the
`django_lazy_migration_bug/test_app/migrations/` files generated by Django
versions 5.x and 6.x, the erroneous behavior is exhibited, and new
migration files are repeatedly generated every time `makemigrations` is
run. Under Django 4.2.27, the field is serialized correctly and repeated
migrations don't occur.

## Investigation

I'm still not sure quite what the root cause of this is. Comparing 5.0 to
4.2.27, there doesn't seem to be significant change in
`django.db.migrations.serializer.py::serializer_factory` that would change
the `MigrationWriter`'s serialization of this field, nor were there any
significant changes to the `MigrationWriter` itself. The first conditional
in `serializer_factory` (concerning the `Promise` `isinstance` check)
would evaluate to true in both versions.There were some changes to
`django.utils.functional.py::lazy`, specifically to the handling of
`resultclasses` `__wrapper__` functions, so maybe that caused some change
in the migration serialization. But I don't see an obvious cause for this
yet.


I have attached to this ticket a patch to the Django unit tests adding a
case for this, which I've confirmed fails currently. As I have time I can
debug further, but I wanted to get the issue reported in case someone else
had a quicker fix than I.
--
Ticket URL: <https://code.djangoproject.com/ticket/36892>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Jan 29, 2026, 6:32:00 PM (8 hours ago) Jan 29
to django-...@googlegroups.com
#36892: Lazy Tuples in field Choices generate repeated migrations with no changes
-------------------------------------+-------------------------------------
Reporter: Matt Armand | Owner: (none)
Type: Bug | Status: new
Component: Migrations | Version: 5.0
Severity: Normal | Resolution:
Keywords: migrations tuple | Triage Stage:
choices lazy functional | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Matt Armand):

* Attachment "django-lazy-migrations-bug-unit-test.diff" added.

Django

unread,
Jan 29, 2026, 9:51:09 PM (5 hours ago) Jan 29
to django-...@googlegroups.com
#36892: Lazy Tuples in field Choices generate repeated migrations with no changes
-------------------------------------+-------------------------------------
Reporter: Matt Armand | Owner: Ahmed
| Shammas J
Type: Bug | Status: assigned
Component: Migrations | Version: 5.0
Severity: Normal | Resolution:
Keywords: migrations tuple | Triage Stage:
choices lazy functional | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Ahmed Shammas J):

* owner: (none) => Ahmed Shammas J
* status: new => assigned

Comment:

I am Ahmed Shammas, a 2nd-year BCA student and GSoC 2026 aspirant. I am
claiming this ticket to investigate the serialization issue with lazy
tuples in migrations. I'll post updates as I make progress.
--
Ticket URL: <https://code.djangoproject.com/ticket/36892#comment:1>

Django

unread,
Jan 29, 2026, 11:34:30 PM (3 hours ago) Jan 29
to django-...@googlegroups.com
#36892: Lazy Tuples in field Choices generate repeated migrations with no changes
-------------------------------------+-------------------------------------
Reporter: Matt Armand | Owner: Ahmed
| Shammas J
Type: Bug | Status: assigned
Component: Migrations | Version: 5.0
Severity: Normal | Resolution:
Keywords: migrations tuple | Triage Stage:
choices lazy functional | Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Ahmed Shammas J):

* has_patch: 0 => 1

Comment:

"I have submitted a PR at https://github.com/django/django/pull/20613
which includes a fix in serializer_factory and a regression test.
--
Ticket URL: <https://code.djangoproject.com/ticket/36892#comment:2>
Reply all
Reply to author
Forward
0 new messages