Django - 解决一个可能的 n + 1 问题

问题描述 投票:0回答:1

我的一个观点是使用特定查询多次访问数据库。 Scout-apm 将其识别为可能的 n+1 查询。我不确定这是问题所在,但它仍然是一个问题。

我原来的代码是:

models.py

class Grade(models.Model):

    score = models.CharField(max_length=4, blank=True)
    testing = models.ForeignKey(Testing, on_delete=models.CASCADE)
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    classroom = models.ForeignKey(Purpose, on_delete=models.CASCADE)
    time_created = models.DateField(
        auto_now=False, auto_now_add=False, default=timezone.now)

Class Testing(models.Model):
    name = models.CharField(max_length=20, blank=True)
    omit = models.BooleanField(default=False)
    
Class Classroom(models.Model):
    name = models.CharField(max_length=20, blank=True)

    

views.py
def allgrades(request, student_id):
    classrooms = Classroom.objects.all()
    for c in classrooms:
        q = Grade.objects.filter(
            student=student_id, classroom=c.id, testing__omit="False").order_by('-time_created')
        if q.exists():
            if len(q) < 3:
                qn = q.first()
            else:
                ....

违规查询来自

if q.exists():
qn = q.first()
。我不知道这是否属于 n + 1 的类别,但是如果我去掉
if q.exists():
qn = q.first()

,查询数量会从 1300 左右减少到 400 左右

我需要这些语句的功能。因为我正在用

q
做一些事情,所以我需要检查它是否存在。正如代码所示,我可能只想包含最新的对象。

有没有更便宜的方法来处理这个问题?

django django-views django-queryset
1个回答
0
投票

你正在通过教室的每个循环进行额外的呼叫。第一个是 .exists(),查看那里是否有东西的成本很低,但如果有并且你需要调用它会变得有点多余,因为检索实际记录是另一个调用(所以最多 2 * num_classrooms).

如果您预取所有内容,则根本不需要进行许多额外的调用:

#set up your grade queryset for clarity
grade_queryset = Grade.objects.filter(
    student=student_id, testing__omit="False"    
).order_by('-time_created')

#get all the Classrooms and their associated grades
classrooms = Classroom.object.prefetch_related(
    Prefetch(
        'grades', 
        queryset=grade_queryset,
    )
 )
#now loop through - django should use its cache for .all() and .first()
for c in classrooms:
    c_grades_count = len(c.grades.all())
    if c_grades_count  < 3:
        qn = c.grades.all()[0]
© www.soinside.com 2019 - 2024. All rights reserved.