尝试创建一种有效的方法来产生具有合并结果的交叉联接

时间:2019-07-19 11:44:26

标签: django python-3.x django-orm cross-join

假设我们有以下模型:

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(.....

我想创建课程和学生的交叉连接,但表中有成绩。

|          | 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

但是在“#注1”点,它每次都会运行一个查询,这会极大地降低性能(一个班级最多可容纳30名学生,每门课程多于50门课程)。

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

1 个答案:

答案 0 :(得分:1)

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

我们可以先查询CourseStudent,然后列出一个二维列表,例如:

courses = Course.objects.all()
students = Student.objects.filter(classroom=classroom)

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=classroom):
    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。表格中第 i,j 个单元格是指courses中第 i 个课程的等级和 j students中的第3个学生。

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

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

,然后像这样渲染:

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

不过,您可以使用django-pivot [PiPy]来完成工作并删除样板代码。