0

過度のデータベース ヒットを回避するために、django でクエリを構造化する最良の方法を理解しようとしています。
これは質問に似ています:外部キー クエリを使用した Django のベスト プラクティスですが、クエリの「深さ」が大きくなります。

私の状況: models.py

class Bucket(models.Model):
    categories = models.ManyToManyField('Category')

class Category(models.Model):
    name = models.CharField(max_length=50)

class SubCategory(models.Model):
    category = models.ForeignKey(Category)

class SubSubCategory(models.Model):
    subcat = models.ForeignKey(SubCategory)

ビュー.py

def showbucket(request, id):
    bucket = Bucket.objects.prefetch_related('categories',).get(pk=id)
    cats = bucket.categories.prefetch_related('subcategory_set__subsubcategory_set',)
    return render_to_response('showbucket.html', locals(), context_instance=RequestContext(request))

および関連するテンプレート:

{% for c in cats %}
    {{c}}
    <ul>
    {% for d in c.subcategory_set.all %}
        <li>{{d}}</li>
        <ul>
        {% for e in d.subsubcategory_set.all %}
            <li>{{e}}</li>
        {% endfor %}
        </ul>
    {% endfor %}
    </ul>
{% endfor %}

prefetch_related() を使用しているにもかかわらず、上位 2 つの for ステートメントが評価されるたびにデータベースにアクセスしているように見えます (例: {% for c in cat %}) (少なくとも、debug_toolbar を読むとそう思います)。私が試した他の方法では、(C x D x E) 件のデータベース ヒットが発生しました。これは、プリフェッチ、クエリ、またはモデルの使用に本質的に問題がありますか? いわば「深さ> 1」のデータベースオブジェクトにアクセスするDjangoの最良の方法は何ですか?

4

2 に答える 2

3

代わりにselect_related()を使用してください。

https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.select_related

bucket = Bucket.objects.select_related('categories',).get(id=id)
cats = bucket.categories.select_related('subcategory_set__subsubcategory_set',)
于 2013-03-20T02:11:55.653 に答える
0

だから、ここでいくつかのことが起こっていることがわかりました:

まず、select_related と prefetch_related に関する私の現在の理解:

select_related()は外部キーの関係に従い、結果セットが大きくなりますが、後で FK を使用しても追加のデータベース ヒットが必要ないことを意味します。FK および 1 対 1 の関係に限定されます。

prefetch_related() は、関係ごとに個別のルックアップを行い、それらを Python で結合します。これは、多対多、多対 1、および GenericRelation と GenericForeignKey に使用される手段です。

本によると、外部キーを「フォロー」していなかったので、prefetch() を使用する必要があります。これは私が理解していることですが、{with} タグの使用を追加した場合でも、テンプレートで指定された for ループを評価するときに、テンプレートが追加のクエリを引き起こしているように見えました。

最初は、この問題に似たものを発見したと思っていましたが、単純化した例を作成したときに再現できませんでした。デバッグ ツールバーの使用から、次のテンプレート コードを使用した直接チェックに切り替えました (Karen Tracey による Django を使用したリクエストの SQL クエリの追跡の記事では、リンクしますが、リンクが制限されています)。

{% with sql_queries|length as qcount %}
{{ qcount }} quer{{ qcount|pluralize:"y,ies" }}
{% for qdict in sql_queries %}
{{ qdict.sql }} ({{ qdict.time }} seconds)
{% endfor %}
{% endwith %}

この方法を使用すると、pre-fetch() を使用するクエリが 5 つしか表示されず (debug_toolbar で 7 つ)、select_related() を使用するとクエリが直線的に増加します (debug_toolbar で +2)。

この種の問題に対処するための他のアドバイスやツールを喜んで受け入れます。

于 2013-03-20T20:07:18.123 に答える