0

私は次のモデルを持っています:

個人モデルには、質問との多対多(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")

例外として、PersonテーブルとQuestionsテーブルの2つのクエリを実行します。

ただし、テンプレート内のリストをトラバースすると、Personの各行に対して追加のselectクエリが実行されます。

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

これは質問の2番目の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たので、で追加のフィルタリングが行われないことは間違いありませんQuerySet

注意すべき点の1つは、結合テーブルを自分で明示的に生成しないことです。これが問題になる可能性がありますか?(を使用してthrough

4

1 に答える 1

0

私はこれと同じ問題を抱えていました: 何かを参照しようとするたびに追加のクエリを実行していました。私は解決策を見つけたと思います。各インスタンスの追加のルックアップを防ぐために、 prefetch_related を拡張して、必要なルックアップ テーブルを含めることができます。確かに、これによりもう 1 つのクエリが生成されますが、アクセスしようとしている人が 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 を使用して個人レコードのすべてのルックアップ テーブル (1 対 1) を取得できます。次に、(personethnicity と studentsemester の) prefetch_related を使用して多対多を取得できます。また、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 に答える