5

最適化しようとしているDjangoビューがあります。ページ上の親オブジェクトのリストとその子が表示されます。子モデルには親への外部キーがあるため、select_related適用されないようです。

class Parent(models.Model):
    name = models.CharField(max_length=31)

class Child(models.Model):
    name = models.CharField(max_length=31)
    parent = models.ForeignKey(Parent)

単純な実装では、 n + 1クエリを使用します。ここで、nは親オブジェクトの数です。親リストをフェッチするための1つのクエリ、次に各親の子をフェッチするための1つのクエリ。

私は2つのクエリで仕事をするビューを書きました-1つは親オブジェクトをフェッチし、もう1つは関連する子をフェッチし、次にいくつかのPython(ここに投稿するにはあまりにも恥ずかしいです)ですべてを元に戻します。

標準ライブラリのcollectionsモジュールをインポートしていることに気付いたとき、おそらくそれが間違っていることに気づきました。おそらくもっと簡単な方法がありますが、私はそれを見つけるためのDjangoの経験が不足しています。どんなポインタでも大歓迎です!

4

3 に答える 3

3

related_name外部キーにaを追加してから、 prefetch_relatedDjango1.4に追加した方法を使用します。

QuerySet指定された各ルックアップの関連オブジェクトを単一のバッチで自動的に取得するを返します。

select_relatedこれは、関連するオブジェクトへのアクセスによって引き起こされるデータベースクエリの大洪水を防ぐように設計されているという点で、と同様の目的がありますが、戦略はまったく異なります。

  • select_relatedSQL結合を作成し、関連するオブジェクトのフィールドをSELECTステートメントに含めることで機能します。このため、 select_related同じデータベースクエリで関連オブジェクトを取得します。ただし、「多くの」関係を越えて結合することで生じるはるかに大きな結果セットを回避するために、select_related外部キーと1対1の単一値の関係に制限されます。

  • prefetch_related一方、は、関係ごとに個別のルックアップを実行し、Pythonで「結合」を実行します。これにより、でサポートされている外部キーと1対1の関係に加えて、を使用して実行できない多対多および多対1のオブジェクトをプリフェッチできます。また、およびのプリフェッチもサポートしています。select_relatedselect_relatedGenericRelationGenericForeignKey

class Parent(models.Model):
    name = models.CharField(max_length=31)

class Child(models.Model):
    name = models.CharField(max_length=31)
    parent = models.ForeignKey(Parent, related_name='children') 


>>> Parent.objects.all().prefetch_related('children')

関連するすべての子が単一のクエリでフェッチされ、関連する結果のキャッシュが事前に入力されたクエリセットを作成するために使用されます。これらのQuerySetは、self.children.all() 呼び出しで使用されます。

注1は、QuerySetsの場合と同様に、異なるデータベースクエリを暗示する後続のチェーンメソッドは、以前にキャッシュされた結果を無視し、新しいデータベースクエリを使用してデータを取得することに注意してください。

iterator()クエリの実行に使用する場合prefetch_related()、これら2つの最適化は一緒に意味をなさないため、呼び出しは無視されることに注意してください。

于 2012-10-10T07:36:39.380 に答える
3

一度に2つ以上のレベルで作業する必要がある場合は、MPTTを使用してツリーをdbに格納するための別のアプローチを検討できます。

一言で言えば、更新中に更新されるデータをモデルに追加し、はるかに効率的な取得を可能にします。

于 2012-10-10T10:44:29.400 に答える
0

実際、select_relatedはあなたが探しているものです。select_relatedは、必要なすべてのデータが1つのステートメントでフェッチされるようにJOINを作成します。prefetch_relatedは、すべてのクエリを一度に実行してからキャッシュします。

ここでの秘訣は、結合のパフォーマンスの低下を減らすために絶対に必要なものだけを「結合」することです。「絶対に必要なこと」とは、ビューまたはテンプレートで後で読み取るフィールドのみを事前に選択する必要があるという長い言い方です。ここに良いドキュメントがあります:https ://docs.djangoproject.com/en/1.4/ref/models/querysets/#select-related

これは、同様の問題に直面したモデルの1つからのスニペットです。

return QuantitativeResult.objects.select_related(
                'enrollment__subscription__configuration__analyte', 
                'enrollment__subscription__unit', 
                'enrollment__subscription__configuration__analyte__unit',
                'enrollment__subscription__lab',
                'enrollment__subscription__instrument_model'
                'enrollment__subscription__instrument',
                'enrollment__subscription__configuration__method',
                'enrollment__subscription__configuration__reagent',
                'enrollment__subscription__configuration__reagent__manufacturer',
                'enrollment__subscription__instrument_model__instrument__manufacturer'
                ).filter(<snip, snip - stuff edited out>)

この病理学的なケースでは、700以上のクエリから1つに減らしました。この種の問題に関しては、djangoデバッグツールバーがお勧めです。

于 2012-10-10T07:49:21.963 に答える