[Django] #37304: Incorrect migration generated when a JSONField have a local scope defined Decoder

1 view
Skip to first unread message

Django

unread,
Aug 28, 2026, 7:59:31 AM (4 days ago) Aug 28
to django-...@googlegroups.com
#37304: Incorrect migration generated when a JSONField have a local scope defined
Decoder
-------------------------------------+-------------------------------------
Reporter: takuyozora | Type: Bug
Status: new | Component:
| Migrations
Version: 6.1 | Severity: Normal
Keywords: JSONField migration | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Hi,

I have a JSONField on one of my model with a custom JSONDecoder :

{{{#!python
class UnitAbstract(models.Model):
class Meta:
abstract = True
# ...
base_stats = models.JSONField(default=rpg.stats.RPGBaseStats(
pv={'maximum': 100},
mana={'maximum': 0}
).toJSON(), encoder=RPGJSONEncoder,
decoder=rpg.stats.RPGBaseStats.getJSONDecoder())

class Unit(UnitAbstract):
pass
}}}

But the migration file generated by `makemigration` is incorrect (because
of this part :
`decoder=terrarpg.rpg.mixins.DeconstructibleMixin.getJSONDecoder.<locals>.RPGJSONDecoder`)
:


{{{#!python
class Migration(migrations.Migration):

dependencies = [
('terrarpg',
'0035_unitgroup_formation_rank_alter_unit_base_stats_and_more'),
]

operations = [
migrations.AlterField(
model_name='unit',
name='base_stats',
field=models.JSONField(decoder=terrarpg.rpg.mixins.DeconstructibleMixin.getJSONDecoder.<locals>.RPGJSONDecoder,
default=[
#...
], encoder=terrarpg.rpg.parsers.RPGJSONEncoder),
),
}}}

I assume the '''<locals>''' that produce the syntax error is here because
my decoder is a dynamically generated class as you can see here :

{{{#!python
class DeconstructibleMixin(abc.ABC):
# ...
@classmethod
def getJSONDecoder(cls):
class RPGJSONDecoder(json.JSONDecoder):
def decode(self, *args, **kwargs) -> cls:
data = json.JSONDecoder.decode(self, *args, **kwargs)
if isinstance(data, list) and len(data) >= 1:
return cls.fromJSON(data)
return data
return RPGJSONDecoder
}}}

If I do not apply the migration everything is working properly (because
the field have been created before without the custom decoder).
So maybe either Django should not produce a migration file when only the
decoder part change on a JSONField because it's not link to the DB schema,
or it should handle properly the fact that the decoder is a local scope
defined class.

I hope that the bug issue is filled properly and that my English is
compressible enough.
--
Ticket URL: <https://code.djangoproject.com/ticket/37304>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Aug 28, 2026, 8:21:13 AM (4 days ago) Aug 28
to django-...@googlegroups.com
#37304: Incorrect migration generated when a JSONField have a local scope defined
Decoder
-------------------------------------+-------------------------------------
Reporter: takuyozora | Owner: Yassin
| Bahri
Type: Bug | Status: assigned
Component: Migrations | Version: 6.1
Severity: Normal | Resolution:
Keywords: JSONField migration | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Yassin Bahri):

* has_patch: 0 => 1
* owner: (none) => Yassin Bahri
* stage: Unreviewed => Accepted
* status: new => assigned

Comment:

I reproduced this issue and prepared a patch with regression tests.

The issue is that migration serialization currently allows local classes
to be serialized by TypeSerializer, producing invalid migration code
containing `<locals>`. FunctionTypeSerializer already rejects local
functions, so the patch applies the same kind of early failure to local
classes.

The regression tests cover both direct local class serialization and the
reported JSONField(decoder=...) case.

Tested with:

{{{
PYTHONPATH=. python tests/runtests.py migrations.test_writer --verbosity 1
--parallel 1
}}}

Branch: https://github.com/yassinbahri/django/tree/ticket-37304
--
Ticket URL: <https://code.djangoproject.com/ticket/37304#comment:1>
Reply all
Reply to author
Forward
0 new messages