Django迁移文件中PostgreSQL JSONField空默认值的正确声明疑问
你遇到的这个问题正好戳中了Django中可变默认值的经典陷阱,咱们一步步理清楚:
你的迁移代码为什么不符合文档建议?
Django文档反复强调JSONField的默认值必须是可调用对象,而你模型里写的default=dict()和迁移里生成的default={}本质上是同一个问题:它们都是已经实例化的空字典对象。
Python里的可变对象(比如字典、列表)作为默认值时,会被所有类实例共享。也就是说,如果你创建了两个bar实例,它们的foo字段会指向同一个字典——当你修改其中一个实例的foo时,另一个实例的foo也会跟着变,这绝对是你不想看到的隐形bug。
而Django生成迁移时,会把你传入的dict()结果(一个空字典)直接序列化,所以迁移里就变成了default={},和直接写default={}没有任何区别,完全踩了文档警告的坑。
正确的写法是什么?
核心原则是:传递可调用对象本身,而不是它的执行结果。
空字典默认值的最简写法
直接用dict(这是一个可调用的类型对象),每次创建新实例时,Django会自动调用dict()生成一个全新的空字典:foo = JSONField(default=dict)对应的迁移代码会变成:
migrations.AddField( model_name='bar', name='foo', field=django.contrib.postgres.fields.jsonb.JSONField(default=dict), )自定义结构的默认值
如果需要默认值是带有初始键值对的字典,就写一个自定义函数,传递函数名(不要加括号调用):def get_default_foo(): return {"status": "active", "tags": []} foo = JSONField(default=get_default_foo)
已经生成迁移怎么办?
如果迁移还没应用到数据库,建议先回滚这个迁移(用python manage.py migrate <app_name> <previous_migration>),然后修改模型里的默认值写法,重新生成迁移文件。如果已经应用了,那需要生成一个新的迁移来修正默认值,确保后续创建的实例使用正确的可调用默认值。
内容的提问来源于stack exchange,提问作者r3dw00d

