尝试创建一种有效的方法来生成具有组合结果的交叉连接

Trying to create an efficient way to produce a cross join with combined results

假设我们有这些模型:

class ClassRoom(models.Model):
      name = models.CharField(max_length=255)

class Student(models.Model):
      name = models.CharField(max_length=255)
      classroom = models.ForeignKey(ClassRoom......

class Course(models.Model):
      name = models.CharField(max_length=255)

class Grades(models.Model):
      student = models.ForeignKey(Student....
      course = models.ForeignKey(Course....
      grade = models.CharField(.....

我想创建课程和学生的交叉连接,但成绩在 table。

|          | Student A | Student B |
| Course 1 | 8         |           |
| Course 2 | 6         | 4         |

请注意,学生 B 尚未收到课程 1 的成绩!

我目前是这样解决的

query = list(product(courses, students)
grades = Grades.objects.all.....
for i, query_tuple in enumerate(query):
     grade = grades.filter(query_tuple[0], query_tuple[1]
     if grade: # Note 1
         # Here I add it to a list of the grades

但是在“#Note 1”处它每次都运行一个查询,这会大大降低性能(一个 class 最多可以有 30 名学生,每名学生超过 50 门课程)。

有更好的方法吗?也许更多的是 Django-ORM 风格?

。请不要对每个 table 单元格进行查询。这通常不是一个好主意。

我们可以先查询 Courses 和 Students,然后制作一个二维列表,如:

courses = Course.objects.all()
students = Student.objects.filter(classroom=<i>classroom</i>)

coursemap = { c.pk: i for i, c in enumerate(courses) }
studentmap = { s.pk: i for i, s in enumerate(students)}

table = [[None] * len(student) for __ in range(len(courses))]

for grade in Grade.objects.filter(student__classroom=<i>classroom</i>):
    row = coursemap.get(grade.course_id)
    col = coursemap.get(grade.student_id)
    if row is not None and col is not None:
        table[row][col] = grade.grade

所以最后 table 是一个成绩列表,如果没有成绩则有 None。 table 中的第 i,j 个单元格指的是 courses 中第 i 个课程的成绩以及 students.

中的第 j 个学生

然后我们可以像这样传递数据:

return render(
    request,
    'some_template.html',
    {'cols': <b>students</b>, 'rows': <b>zip(students, table)}</b>
)

然后像这样渲染:

<table>
    <thead>
        <tr>
            <th>&times;</th>
            {% for <b>student</b> in cols %}
                <th>{{ <b>student.name</b> }}</th>
            {% endfor %}
        </tr>
    </thead>
    <tbody>
        {% for <b>course, grades</b> in rows %}
            <tr>
                <th>{{ <b>course.name</b> }}</th>
                {% for <b>grade</b> in grades %}
                    <td>{{ <b>grade|default_if_none:''</b> }}</td>
                {% endfor %}
            </tr>
        {% endfor %}
    </tbody>
</table>

但是您可以使用 django-pivot [PiPy] 来完成这项工作并删除样板代码。