为何不同含JSONField的Django Model实例值异常相同?
问题原因分析与解决方案
你碰到的这个情况其实是Django模型字段使用可变对象作为默认值时的一个常见陷阱,我来给你拆解一下:
核心原因
你给JSONField设置的默认值是一个可变的字典对象{'name':'Model'},而Django在初始化模型字段默认值时,对于可变对象并不会每次创建新实例——所有新生成的Mod对象,都会共享同一个字典的引用。
也就是说:
- 创建
m1时,它的data指向了这个默认字典; - 创建
m2时,并没有生成新的字典,而是和m1共享了同一个引用; - 当你修改
m2.data['name']时,其实是直接修改了那个被共享的字典,所以内存里的m1.data也跟着变成了'Model 2'; - 但
m1已经提前保存到数据库了,数据库里存储的是最初的默认值,所以从数据库重新获取m1a时,拿到的是正确的原始数据。
解决方法
要解决这个问题,关键是让每个新实例都拿到独立的默认字典,而不是共享引用,常用的两种方式:
方式一:使用lambda函数作为默认值
这是最简洁的方式,lambda会在每次创建新实例时返回一个新的字典:
from django.db import models from django.contrib.postgres.fields import JSONField class Mod(models.Model): data = JSONField(default=lambda: {'name':'Model'})
方式二:自定义可调用函数返回默认值
如果默认值的逻辑更复杂,可以写一个单独的函数,然后把它作为默认值:
from django.db import models from django.contrib.postgres.fields import JSONField def get_default_data(): # 这里可以添加更复杂的默认值生成逻辑 return {'name':'Model'} class Mod(models.Model): data = JSONField(default=get_default_data)
注意这里不要加括号,直接传函数本身,Django会在需要时自动调用它生成新的默认值。
额外提醒
不止JSONField,所有Django模型字段如果使用**可变对象(比如列表、字典)**作为默认值,都会出现类似的引用共享问题,这是Python和Django的默认值处理机制决定的,属于必须规避的开发陷阱哦。
内容的提问来源于stack exchange,提问作者Jeremy S.
相关产品推荐
相关产品推荐

