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

如何用Django ORM高效实现课程与学生交叉连接并关联成绩?

优化Django中课程-学生成绩交叉表的查询性能

你的问题太典型了——当要生成这种课程×学生的二维成绩交叉表时,循环查询每一组(课程,学生)的成绩确实会踩中N+1查询的大坑(30个学生×50门课就是1500次查询!),完全可以用Django ORM的预取能力+内存映射来解决,下面给你两种高效的实现方式:

方案一:预取所有成绩,内存中构建映射表

这个方法只需要1次查询获取所有相关成绩,然后用字典把(学生ID,课程ID)映射到对应成绩,之后遍历课程-学生组合时直接查字典即可,彻底避免循环中的重复查询:

from itertools import product

# 先获取目标班级的学生和需要展示的课程(假设你已经拿到classroom实例)
students = Student.objects.filter(classroom=classroom).all()
courses = Course.objects.all()  # 可根据需求筛选特定课程

# 一次性拉取所有相关成绩,关联学生和课程(仅1次查询)
grades = Grades.objects.filter(
    student__in=students,
    course__in=courses
).select_related('student', 'course')

# 构建(student_id, course_id) -> grade的字典映射,内存中快速查找
grade_map = {(g.student_id, g.course_id): g.grade for g in grades}

# 生成交叉表数据
cross_table_data = []
for course in courses:
    row = {"course_name": course.name}
    for student in students:
        # 用get方法处理无成绩的情况,返回空字符串
        row[student.name] = grade_map.get((student.id, course.id), "")
    cross_table_data.append(row)

这种方式总共只需要3次查询(学生、课程、成绩),性能比原来的循环查询提升几个数量级。

方案二:用Django ORM直接生成结构化数据(进阶)

如果想更贴合Django ORM的风格,还可以用annotate结合Case/When,让数据库直接为每个学生生成对应的成绩字段,适合学生数量固定的小型班级场景:

from django.db.models import Case, When, Value, CharField

students = Student.objects.filter(classroom=classroom).all()
# 为每个学生创建一个Case/When子句,匹配他们的对应成绩
annotations = {}
for student in students:
    annotations[f"student_{student.id}_grade"] = Case(
        When(grades__student=student, then="grades__grade"),
        default=Value(""),
        output_field=CharField()
    )

# 一次性查询所有课程及其对应每个学生的成绩(仅2次查询)
course_grade_data = Course.objects.annotate(
    **annotations
).prefetch_related("grades__student").distinct()

# 转换为交叉表格式
cross_table_data = []
for course in course_grade_data:
    row = {"course_name": course.name}
    for student in students:
        row[student.name] = getattr(course, f"student_{student.id}_grade")
    cross_table_data.append(row)

这个方法把成绩匹配逻辑下推到数据库处理,同样能彻底解决N+1问题。

两种方案的对比

  • 方案一更灵活,适合学生或课程数量动态变化的场景,内存处理逻辑直观易懂;
  • 方案二更贴近ORM风格,依赖数据库的聚合能力,适合学生数量固定的小型班级。

不管选哪种,都能把你的查询次数从1500+降到3次以内,性能提升非常明显。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:19:40