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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 06:48:03