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
相关产品推荐
相关产品推荐

