【问题标题】:Trying to create an efficient way to produce a cross join with combined results尝试创建一种有效的方法来产生具有组合结果的交叉连接
【发布时间】:2019-11-28 09:54:18
【问题描述】:

假设我们有这些模型:

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

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

有没有更好的方法来做到这一点?也许更多的是 Django-ORM 风格?

【问题讨论】:

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


    【解决方案1】:

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

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

    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的学生。

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

    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] 来完成这项工作并删除样板代码。

    【讨论】:

    • 在过去的 30 分钟里,我一直试图让它工作,但我似乎无法弄清楚。我得到了一张人满为患的桌子,这很棒,就像我已经非常感谢你一样!但是,当试图在模板中显示表格时遇到问题,当表格中只有 id 时,我将如何打印课程/学生姓名?您通常不能从 pk 返回对象,对吗?是否正在编写一个函数来获取您推荐的对象?
    • @MrDikke:这应该类似于更新答案中的模板。
    • 是的,我想我不会教那个!非常感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-15
    • 1970-01-01
    • 2021-12-17
    • 2012-10-07
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    相关资源
    最近更新 更多