1

django db クエリの最適化に関する初心者の質問:

Destination オブジェクトを編集するためのカスタム モデル フォームがあり、コンストラクターで、関連する Visitor モデルからクエリセットを取得します。このモデルには、Destination に ManyToMany フィールドがあります (カスタム モデル フォームを使用する理由については、EDIT を参照してください)。

    print "loading initial choices"
    visitor_choices, visitor_initial = [], []
    visitor_set = self.instance.visitor_set.all()
    print visitor_set
    for obj in Visitor.objects.all():
        visitor_choices.append((obj.pk, obj.name))
        #if visitor_set.filter(pk=obj.pk # this hits the db every time!
        if obj in visitor_set:
            visitor_initial.append(obj.pk)

    self.fields['visitors'].choices = visitor_choices
    self.fields['visitors'].initial = visitor_initial

    print "finished loading initial choices"

アイデアは、関連する Visitor_set を変数にロードして、各訪問者が Visitor_set に存在するかどうかを確認するためのクエリの繰り返しを避けることでした。これは最善のアプローチですか?

また、データベース ロギングをオンにすると (この質問の 2 番目の回答で説明されているように)、目的地 ID 1 のすべての訪問者を選択するクエリ (3 番目の SELECT ステートメント) が繰り返されるのがわかりますが、これは私が書いたコードのどこにもありません。それはどこから来たのですか?

loading initial choices
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1  LIMIT 21; args=(1,
)
[<Visitor: MIMA>, <Visitor: MIMO>, <Visitor: MIMU>]
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor"; args=()
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)
finished loading initial choices

編集

Destination私が参照するオブジェクトは、オブジェクトのフィールドの関連する側ManyToManyですVisitor。私のフォームが Visitor オブジェクト自体を編集していた場合、Django はManyToManyフィールドを自動的に処理します。しかし、モデル フォームでこれを行うにはDestination、複数選択フィールドをVisitor追加し、メソッドをカスタマイズし__init__て選択肢と初期選択をロードする必要があります。

ただし、質問はクエリセットの処理方法と、シェルからも確認できる manytomany 値をロードするための不思議な 2 番目の SQL に関するものです。

>>> from testapp.forms import DestinationForm
>>> from testapp.models import Destination, Visitor
>>> dest = Destination.objects.get(pk=1)
(0.001) SELECT "testapp_destination"."id", "testapp_destination"."destination" FROM "testapp_destination" WHERE "testapp_destination"."id" =
 1 ; args=(1,)
>>> destinationForm = DestinationForm(instance=dest)
loading initial choices
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testap
p_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1  LIMIT 21; args=(1,
)
[<Visitor: MIMA>, <Visitor: MIMO>, <Visitor: MIMU>]
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor"; args=()
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testap
p_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)
finished loading initial choices
>>>

ありがとう

4

1 に答える 1

1

あなたの言うことに答えるのがあなたの質問です:私は質問だと思います

SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)

ラインから来る

if obj in visitor_set:

ここで、Django は次のクエリを再実行しますvisitor_set( Django ドキュメントでクエリセットが評価されるときを参照してください)。visitor_set次のように、setすぐに変換することでこれを回避できます(したがって、Django はクエリをすぐに実行するように強制されます)。

visitor_set = set(self.instance.visitor_set.all())

これにより、オブジェクトがこのセットに含まれているかどうかをテストするパフォーマンスも向上します (listまたは同様の iterable と比較して)。

于 2011-12-14T10:02:08.890 に答える