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

Django数据库设计规范化及嵌套成绩数据返回实现问题

Q1 表拆分方案选择

你当前的单表结构存在明确的数据冗余,同一个学生的姓名、年龄会重复存储N次(N等于该学生的参考科目数),后续如果需要修改学生年龄等基础信息,需要更新他对应的所有成绩行,非常容易出现数据不一致的问题,所以表拆分不属于过度优化,是避免后期业务迭代出问题的必要设计,具体怎么选可以结合你的业务场景判断:

  • 如果你的核心业务就是围绕学生成绩管理,后续还要扩展班级、学号、科目学分这类属性,优先在应用层通过Django ORM做规范拆分,建议拆3张表:
    • student表:存储学生唯一ID、姓名、年龄,避免基础信息冗余
    • subject表:存储科目唯一ID、科目名称,后续新增科目直接加行即可,无需修改表结构
    • grade事实表:存储ID、学生ID外键、科目ID外键、成绩
      这种结构后续增删改逻辑清晰,ORM关联查询方便,长期维护成本远低于单表结构。
  • 如果你这个工具只是临时使用的小工具,后续没有迭代计划,且总数据量在千条以内,也可以保留现有单表结构,把规范化处理放到数据仓库环节。但只要后续有业务扩展可能,都不建议这么做,后期再改数据结构的迁移成本会高出很多。

Q2 Django实现嵌套格式返回

这里分两种场景给出实现方案:

场景1:保留现有单表结构

假设你的ORM模型定义如下:

from django.db import models

class Grade(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    subject = models.CharField(max_length=100)
    grade = models.CharField(max_length=2)

视图层代码示例(Django原生实现,无需引入额外组件):

from django.http import JsonResponse

def get_student_grades(request, name):
    # 过滤目标学生的所有成绩记录
    grade_records = Grade.objects.filter(name=name)
    if not grade_records.exists():
        return JsonResponse({"error": "学生不存在"}, status=404)
    # 提取学生公共基础信息
    base_info = grade_records.first()
    # 组装成绩字典
    grades_dict = {record.subject.lower(): record.grade for record in grade_records}
    # 拼接返回结构
    result = {
        "name": base_info.name,
        "age": base_info.age,
        "grades": grades_dict
    }
    return JsonResponse(result)

路由配置(urls.py):

urlpatterns = [
    # 其他路由
    path('grades/<str:name>/', get_student_grades, name='student_grades'),
]

场景2:拆分多表后的实现

如果已经拆分3张表,ORM模型带外键关联:

from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

class Subject(models.Model):
    name = models.CharField(max_length=100)

class Grade(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='grades')
    subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
    grade = models.CharField(max_length=2)

用prefetch_related优化查询,避免N+1问题:

from django.http import JsonResponse
from django.db.models import Prefetch

def get_student_grades(request, name):
    try:
        student = Student.objects.filter(name=name).prefetch_related(
            Prefetch('grades', queryset=Grade.objects.select_related('subject'))
        ).get()
    except Student.DoesNotExist:
        return JsonResponse({"error": "学生不存在"}, status=404)
    
    grades_dict = {grade.subject.name.lower(): grade.grade for grade in student.grades.all()}
    result = {
        "name": student.name,
        "age": student.age,
        "grades": grades_dict
    }
    return JsonResponse(result)

如果使用Django REST Framework,直接定义嵌套序列化器即可实现,代码会更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 13:15:03