Django models.py中如何实现类似C语言的结构体嵌套功能
实现方案
方案1:JSONField + 数据类封装(开发成本最低,无需额外数据库字段)
该方案直接把24个小时的属性存在JSON字段内,通过封装属性访问逻辑完全匹配你需要的调用方式,不需要建立额外数据表也不需要使用外键。
首先将PeriodProperties改为普通数据类,无需继承Django模型:
from dataclasses import dataclass from django.db import models @dataclass class PeriodProperties: isX: bool = False isY: bool = False isZ: bool = False R: float = 0.0 P: float = 0.0 # 转换为字典用于存入JSON字段 def to_dict(self): return { "isX": self.isX, "isY": self.isY, "isZ": self.isZ, "R": self.R, "P": self.P }
再实现OneDay模型,通过魔术方法动态生成hour1-hour24的访问属性:
class OneDay(models.Model): # 用单个JSON字段存储24小时的全量属性,默认值为空字典 hour_data = models.JSONField(default=dict) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 初始化时为无数据的小时补默认值 for i in range(1, 25): hour_key = f"hour{i}" if hour_key not in self.hour_data: self.hour_data[hour_key] = PeriodProperties().to_dict() # 处理hourX属性的读取逻辑 def __getattr__(self, name): if name.startswith("hour") and name[4:].isdigit(): hour_num = int(name[4:]) if 1 <= hour_num <= 24: return PeriodProperties(**self.hour_data[name]) return super().__getattr__(name) # 处理hourX属性的赋值逻辑 def __setattr__(self, name, value): if name.startswith("hour") and name[4:].isdigit(): hour_num = int(name[4:]) if 1 <= hour_num <= 24 and isinstance(value, PeriodProperties): self.hour_data[name] = value.to_dict() return super().__setattr__(name, value)
使用方式完全符合你的需求:
# 示例用法 one_day = OneDay.objects.create() # 读取属性 print(one_day.hour5.isX) # 默认输出False print(one_day.hour10.R > 0.5) # 对应你需要的50%阈值判断 # 修改属性 one_day.hour3 = PeriodProperties(isX=True, R=0.6, P=0.2) one_day.save()
该方案优势是代码量少、结构简洁,劣势是如果需要针对单个小时的单个属性做数据库查询,需要使用JSON字段专用的查询语法。
方案2:独立字段 + 属性代理(适合高频按单个属性查询的场景)
如果你需要对属性加数据库索引、做高频条件查询,可以把所有属性展开为独立数据库字段,再做属性代理实现需求,同样不需要外键和额外表:
from dataclasses import dataclass from django.db import models @dataclass class PeriodProperties: isX: bool isY: bool isZ: bool R: float P: float hour_num: int instance: models.Model # 修改属性时直接同步到模型对应的数据库字段 def __setattr__(self, name, value): if name in ["isX", "isY", "isZ", "R", "P"]: setattr(self.instance, f"hour{self.hour_num}_{name}", value) return super().__setattr__(name, value) class OneDay(models.Model): # 动态生成24小时对应的全部数据库字段 for hour in range(1, 25): locals()[f"hour{hour}_isX"] = models.BooleanField(default=False) locals()[f"hour{hour}_isY"] = models.BooleanField(default=False) locals()[f"hour{hour}_isZ"] = models.BooleanField(default=False) locals()[f"hour{hour}_R"] = models.FloatField(default=0.0) locals()[f"hour{hour}_P"] = models.FloatField(default=0.0) del hour # 清理循环变量,避免生成多余类属性 def __getattr__(self, name): if name.startswith("hour") and name[4:].isdigit(): hour_num = int(name[4:]) if 1 <= hour_num <= 24: return PeriodProperties( isX=getattr(self, f"hour{hour_num}_isX"), isY=getattr(self, f"hour{hour_num}_isY"), isZ=getattr(self, f"hour{hour_num}_isZ"), R=getattr(self, f"hour{hour_num}_R"), P=getattr(self, f"hour{hour_num}_P"), hour_num=hour_num, instance=self ) return super().__getattr__(name)
该方案优势是所有属性都是独立数据库字段,可以直接用ORM做条件查询,比如OneDay.objects.filter(hour5_isX=True, hour10_R__gt=0.5)就能直接匹配你需要的查询条件,劣势是数据表字段较多,但120个字段完全在Django和主流数据库的承载范围内。
内容的提问来源于stack exchange,提问作者kashix9
相关产品推荐
相关产品推荐

