2012-08-16 91 views
0

我有以下型號:Django prefetch_related造成M2M關係中的額外查詢

人員模型與問題具有多對多關係(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表,爲例外另一個問題表。

但是,如果我遍歷模板中的列表,則會爲每行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 

我禁用所有自定義Manager S,所以我敢肯定額外的濾波未在QuerySet完成。

有一點要注意的是,我沒有明確地生成連接表我自己,這可能是問題嗎? (使用through

+0

問題模型的外觀如何?它可能會以某種方式引用人員模型,使其再次查詢數據庫。 – Exelian 2012-08-17 10:22:40

回答

0

我遇到了同樣的問題:每次嘗試引用某些我認爲會在查詢中拉回的內容時,我都執行了其他查詢!我想我找到了解決方案。爲了防止每個實例的額外查找,可以擴展prefetch_related以包含所需的查找表。事實上,這產生多一個查詢,但它不會產生10,000個查詢,假設您有10000人正在嘗試訪問。

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(個人風格和studentsemester)我可以通過執行personethnicity__ethnicity等獲得與那些多對多表匹配的查找表。

在一個查詢中,返回8000個學生左右,我只用這個代碼做了15個SQL查詢。然後,我做我的引用預取字段(在我的情況下,學生螺柱環內)是這樣的:

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

希望這有助於。