web-dev-qa-db-ja.com

Djangoの関連オブジェクトの反復:クエリセットのループまたは1行のselect_related(またはprefetch_related)の使用

ニュースレターの各号内に複数の記事があるニュースレターアプリケーションがあります。ニュースレターの年、巻、ラベルを一覧表示する概要ページをオンラインで表示し、その後、順不同リストに問題のすべての記事を表示したいと思います。私はDjangoにかなり慣れているので、これを行うための最良の方法を決定しようとしています。

モデルを定義しました(関連する部分のみ):

_Models.py_:

_class Newsletter(models.Model):
    volume = models.ForeignKey(Volume)
    year   = models.IntegerField()
    season = models.CharField(max_length=6, choices=VOLUME_SEASON)
    label  = models.CharField(max_length=20)
    number = models.IntegerField()

class Article(models.Model):
    newsletter = models.ForeignKey(Newsletter)
    section    = models.ForeignKey(Section)
    title      = models.CharField(max_length=200)
_

私がウェブで見たいものは次のようになります:

_<h2>Spring 2012</h2>
<p>Volume 14, Number 1</p>
<ul>
    <li>Foo</li>
    <li>Bar</li>
    <li>Baz</li>
</ul>

<h2>Winter 2011</h2>
<p>Volume 13, Number 4</p>
<ul>
  <li>Boffo</li>
</ul>
_

ものすごく単純。しかし、私は自分の見解を書く最良の方法に戸惑っています。使用するかどうか:

  • 私がZip()である2つのリストと、テンプレートで反復
  • select_related()クエリセットを使用する
  • prefetch_related()クエリセットを使用する

私は最初のオプションを使用してそれを働いています:

_Views.py_:

_from Django.shortcuts import render_to_response, get_object_or_404
from www.apps.newsletter.models import Newsletter, Article

def index(request):
    article_group = []
    newsletter = Newsletter.objects.all().order_by('-year', '-number')
    for n in newsletter:
        article_group.append(n.article_set.all())
    articles_per_newsletter = Zip(newsletter, article_group)

    return render_to_response('newsletter/newsletter_list.html',
                              {'newsletter_list': articles_per_newsletter})
_

次に、次のテンプレートを使用してレンダリングします。

_Newsletter_list.html_:

_{% block content %}
  {% for newsletter, articles in newsletter_list %}
    <h2>{{ newsletter.label }}</h2>
    <p>Volume {{ newsletter.volume }}, Number {{ newsletter.number }}</p>
    <p>{{ newsletter.article }}</p>
    <ul>
    {% for a in articles %}
      <li>{{ a.title }}</li>
    {% endfor %}
    </ul>
  {% endfor %}
{% endblock %}
_

かなり単純明快ですが、Django=に慣れていないので、私がしていることはその強力なORMに関して完全に非効率的であるのではないかと思っていました。より高速な方法がある場合は、2つのリストを一緒に飛ばしてZip()を一緒に実行します。

TIA。

16
tatlar

現在実行しているアプローチはheavly非効率になります。これは1 + Nのクエリ数になるためです。つまり、すべてのニュースレターのクエリに1つ、次にn.article_set.all()の結果を評価するたびに1つになります。したがって、最初のクエリに100個のNewletterオブジェクトがある場合、101個のクエリを実行します。

これがprefetch_relatedを使用する優れた理由です。結果は2つのクエリのみになります。 1つはニュースレターを取得するため、もう1つは関連する記事をバッチで取得するためのものです。 Zipを実行してそれらを整理することは完全に可能ですが、既にキャッシュされているので、実際にはクエリをテンプレートに直接渡してループさせることができます。 :

ビュー

newsletters = Newsletter.objects.prefetch_related('article_set').all()\
                    .order_by('-year', '-number')

return render_to_response('newsletter/newsletter_list.html',
                          {'newsletter_list': newsletters})

テンプレート

{% block content %}
  {% for newsletter in newsletter_list %}
    <h2>{{ newsletter.label }}</h2>
    <p>Volume {{ newsletter.volume }}, Number {{ newsletter.number }}</p>
    <p>{{ newsletter.article }}</p>
    <ul>
    {% for a in newsletter.article_set.all %}
      <li>{{ a.title }}</li>
    {% endfor %}
    </ul>
  {% endfor %}
{% endblock %}
45
jdi