0

我有以下型号:

Person 模型与 Questions 有多对多 (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,所以预取肯定是有效的,但不知何故,它需要额外的查询来显示每个问题。

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

我禁用了所有 custom Manager,所以我很确定没有在QuerySet.

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

4

1 回答 1

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)来获取我的多对多,并且我可以通过执行 personethnicity__ethnicity 等来获取与这些多对多表一起使用的查找表。

在一个大约有 8000 名学生的查询中,我只用这段代码执行了 15 个 SQL 查询。然后我通过这样做(在我的情况下,在学生中的螺柱循环内)引用我的预取字段,如下所示:

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

希望这可以帮助。

于 2012-12-20T17:00:56.960 回答