You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让自定义orion_field像Django字段一样作用于实例而非类?

搞定这个实例属性共享的问题!

你遇到的问题其实不是orion_field把属性搞成类级别了,而是不小心踩了Django模型里可变默认值的经典坑!

为啥会出现这个奇怪的共享现象?

看你的KPI模型里的data字段:

data = JSONField(blank=True, default={})

Django在处理模型字段的默认值时,如果用的是字典、列表这种可变对象,这个对象会在类定义的时候就创建出来,所有新生成的实例都会共享这同一个对象。也就是说,orion_entity1和orion_entity2的data其实指向的是同一个字典!你改了其中一个,另一个自然也会跟着变。

怎么解决?

第一步:修复JSONField的默认值

把可变的{}换成可调用的函数,让每个实例初始化时都生成一个全新的空字典:

class KPI(models.Model):
    orion_id = models.CharField(primary_key=True, unique=True, max_length=10)
    # 用lambda返回新字典,或者写个单独的函数都可以
    data = JSONField(blank=True, default=lambda: {})
    name = orion_field("name")

    def set(self, attribute, value):
        self.data[attribute]= value
    def get(self, attribute):
        return self.data[attribute]

这样每个新实例的data都是独立的,再也不会互相干扰了。

第二步:验证效果

现在再跑你的测试代码:

orion_entity1 = KPI()
orion_entity2 = KPI()
orion_entity1.name = "Babar"
print(orion_entity2.name)
>>> KeyError: 'name'  # 这才是正常的,因为第二个实例的data里根本没name这个键

如果不想看到KeyError,可以给get方法加个默认值:

def get(self, attribute):
    return self.data.get(attribute)  # 不存在的话返回None

顺便优化下你的orion_field

当前的逻辑没问题,但可以稍微调整下让它更健壮:

def orion_field(name, field=None):
    def getter(self):
        return self.data.get(name)  # 用get避免KeyError
    def setter(self, value):
        self.data[name] = value
    return property(getter, setter)

进阶:如果想让orion_field更像Django原生字段

要是你希望这个自定义字段能在admin后台显示、支持序列化等,可以考虑继承Django的Field类实现自定义字段,但如果只是内部业务使用,当前的property方式完全够用啦。

内容的提问来源于stack exchange,提问作者Nasgar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:28:45