Django prefetch_related导致M2M关系中的额外查询

时间:2012-08-16 18:58:41

标签: django django-models

我有以下型号:

人物模型与问题有很多对话(M2M)。

class Person(models.Model):
    name = models.CharField(max_length=100)
    questions = models.ManyToManyField(Question, related_name='persons')

我从Django调试工具栏中看到,当我发出:

persons = Person.objects.filter(name="Foo").prefetch_related("questions")

它执行2个查询,一个到Person表,另一个到Questions表作为例外。

但是,如果我遍历模板中的列表,则会对每行Person进行额外的选择查询。

{% for person in persons %}
{{ person.name }}
   {%  for question in person.questions.all %}
       {{ question.text }}
   {% endfor %}
{% endfor %}

这是问题的第二个SQL,所以肯定prefetch正在工作,但不知何故它需要额外的查询来显示每个问题。

SELECT ("myapp_person_questions"."person_id") AS "_prefetch_related_val", 
"myapp_question"."id", "myapp_question"."asker_id", 
 "myapp_question"."question", "myapp_question"."timestamp" 
INNER JOIN "myapp_person_questions" ON ("myapp_question"."id" = 
 "myapp_person_questions"."question_id") WHERE              
 "myapp_person_questions"."person_id" IN (1, 2, 3, 4) ORDER BY 
 "myapp_question"."timestamp" DESC


SELECT "myapp_question"."id", "myapp_question"."asker_id", 
"myapp_question"."question", "myapp_question"."timestamp" FROM 
"myapp_question" INNER JOIN "myapp_person_questions" 
ON ("myapp_question"."id" = "myapp_person_questions"."question_id") 
WHERE "myapp_person_questions"."person_id" = 1 ORDER BY "myapp_question"."timestamp" DESC

我已停用所有自定义Manager,因此我非常确定QuerySet上没有进行其他过滤。

有一点需要注意的是,我自己没有明确生成连接表,这可能是问题吗? (使用through

1 个答案:

答案 0 :(得分:0)

我遇到了同样的问题:每当我尝试引用我认为已经在查询中撤回的内容时,我正在执行其他查询!我想我找到了解决方案。为了防止对每个实例进行额外查找,您可以扩展prefetch_related以包含所需的查找表。实际上,这会产生一个查询,但它不会产生10,000个查询,假设您有10,000个人正在尝试访问。

    students = Person.objects.select_related('gender',
                                             'old_race',
                                             'ethnicity',
                                             'father_education_level',
                                             'mother_education_level',
                                             ).prefetch_related('personethnicity_set',
                                                                'personethnicity_set__ethnicity',
                                                                'studentsemester_set',
                                                                'studentsemester_set__semester',
                                                                'studentsemester_set__first_mode_admission',
                                                                'studentsemester_set__classification',
                                                                'studentsemester_set__academic_status',
                                                                'studentsemester_set__studentsemestermajor_set',
                                                                'studentsemester_set__studentsemestermajor_set__major_type',
                                                                'studentsemester_set__studentsemestermajor_set__major',
                                                                'studentsemester_set__studentsemestermajor_set__major__registrar_school').filter(ftic_cohort=oursem).order_by('-studentsemester__semester__ccyys')

在上面的代码中,我能够使用select_related获取我的人员记录的所有查找表(一对一)。然后,我能够使用prefetch_related(personethnicity和studentsemester),来获取我的多个manys,我能够获得与那些多对多表一起使用的查找表。做人性,有同情心等等。

在一个回收8000名学生的查询中,我只使用此代码执行15次SQL查询。然后我通过这样做来引用我的预取字段(在我的例子中,在学生中的一个循环中):

studict['ethnicities'] = ''
for myeth in stud.personethnicity_set.all(): 
    studict['ethnicities'] += str(myeth.ethnicity) + ' '

希望这有帮助。