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

Django题型与子题型模型架构合理性评估及优化咨询

Django题型与子题型模型架构评估与优化建议

现有架构合理性分析

你的模型架构整体思路可行,符合业务需求:

  • 核心关联逻辑正确:Question强制关联QuestionType,子题型QuestionSubType设为可选,满足"必须关联题型、子题型可选"的要求
  • 分层设计合理:将题型、子题型拆分为独立模型,避免在Question中存储冗余的题型文本,便于后续统一维护题型列表

潜在问题

  1. 数据完整性无约束:目前无法保证Question中选中的type_subtype属于对应的type,可能出现"题型选了选择题,子题型却选了填空题下的细分类型"的错误数据
  2. 字段命名冗余:QuestionType.question_type、QuestionSubType.question_sub_type这类命名重复,可读性差;Question里的type、type_subtype字段名不够直观
  3. 缺少必要方法与约束:QuestionSubType没有__str__方法,在后台管理或打印对象时无法直观显示子题型信息;且未限制同一题型下不能出现重复的子题型名称
  4. __str__方法存在报错风险:如果chapter为null,self.chapter.subject.grade会触发AttributeError

优化建议

1. 新增数据完整性校验

在Question模型中添加clean方法,确保子题型与题型匹配,同时在表单验证时生效:

from django.core.exceptions import ValidationError

class Question(QuestionAbstractModel):
    # 其他字段保持不变
    def clean(self):
        super().clean()
        # 当子题型存在时,校验其所属题型是否与当前题型一致
        if self.type_subtype and self.type_subtype.question_type != self.type:
            raise ValidationError({"type_subtype": "子题型必须属于当前选中的题型"})

2. 优化字段命名与模型方法

调整字段名提升可读性,为QuestionSubType添加__str__方法与唯一约束:

class QuestionType(models.Model):
    name = models.CharField(max_length=255, verbose_name="题型名称")
    
    def __str__(self):
        return self.name

class QuestionSubType(models.Model):
    question_type = models.ForeignKey(QuestionType, on_delete=models.CASCADE, verbose_name="所属题型")
    name = models.CharField(max_length=255, verbose_name="子题型名称")
    
    def __str__(self):
        return f"{self.question_type.name} - {self.name}"
    
    class Meta:
        verbose_name = "子题型"
        verbose_name_plural = "子题型"
        # 确保同一题型下子题型名称唯一
        unique_together = ("question_type", "name")

class Question(QuestionAbstractModel):
    chapter = models.ForeignKey(Chapter, blank=True, null=True, on_delete=models.CASCADE, verbose_name="所属章节")
    question_type = models.ForeignKey(QuestionType, on_delete=models.CASCADE, blank=False, verbose_name="题型")
    question_subtype = models.ForeignKey(QuestionSubType, on_delete=models.CASCADE, blank=True, null=True, verbose_name="子题型")
    solution_url = models.URLField(max_length=555, blank=True, verbose_name="解析链接")
    
    def __str__(self):
        # 处理chapter为null的情况,避免报错
        if self.chapter:
            return f"{self.chapter.subject.grade} {self.chapter.subject.name} {self.chapter.name} {self.question_type}"
        return f"无章节 - {self.question_type}"

3. 可选优化:后台管理体验提升

在Django Admin中,通过自定义表单实现子题型随题型动态加载(需要前端配合),避免用户选择不匹配的子题型,进一步降低错误数据的概率。

4. 模型选型补充

如果你的题型列表固定且不会频繁变更,也可以考虑用choices字段替代QuestionType模型,但从扩展性角度看,当前的独立模型方案更适合需要动态增删题型的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:57:43