尝试创建一种有效的方法来生成具有组合结果的交叉连接
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 单元格进行查询。这通常不是一个好主意。
我们可以先查询 Course
s 和 Student
s,然后制作一个二维列表,如:
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>×</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] 来完成这项工作并删除样板代码。
假设我们有这些模型:
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 单元格进行查询。这通常不是一个好主意。
我们可以先查询 Course
s 和 Student
s,然后制作一个二维列表,如:
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
.
然后我们可以像这样传递数据:
return render(
request,
'some_template.html',
{'cols': <b>students</b>, 'rows': <b>zip(students, table)}</b>
)
然后像这样渲染:
<table>
<thead>
<tr>
<th>×</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] 来完成这项工作并删除样板代码。