迭代Django中的相关对象:循环查询集或使用单行select_related(或prefetch_related)

tat*_*lar 15 python django orm

我有一个时事通讯应用程序,其中一个时事通讯在每个问题中都有多篇文章.我想在线显示一个摘要页面,列出时事通讯年份,数量和标签,然后在无序列表中显示问题中的所有文章.我对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)
Run Code Online (Sandbox Code Playgroud)

我想在网上看到的内容如下:

<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>
Run Code Online (Sandbox Code Playgroud)

很简单.但是,我对编写视图的最佳方式感到困惑.是否使用:

  • zip()在模板中迭代的两个列表
  • 使用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})
Run Code Online (Sandbox Code Playgroud)

然后使用以下模板渲染它:

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 %}
Run Code Online (Sandbox Code Playgroud)

非常简单,但由于我对Django很新,我想知道我所做的事情在强大的ORM方面是否完全没有效率.zip()如果有更快的方法,我很乐意不必在飞行中制作一个列表然后将两个列表放在一起.

TIA.

jdi*_*jdi 44

你现在正在做的方法将是严重低效的,因为这将导致1 + N号查询.也就是说,1用于查询所有简报,然后每次评估这些n.article_set.all()结果时为1.因此,如果您在第一个查询中有100个Newletter对象,那么您将执行101个查询.

这是一个很好的理由prefetch_related.它只会导致2个查询.一个获取通讯,一个批量获取相关文章.虽然你仍然完全能够继续zip组织它们,但它们已经被缓存了,所以你真的可以直接将查询传递给模板并在其上循环.:

视图

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

return render_to_response('newsletter/newsletter_list.html',
                          {'newsletter_list': newsletters})
Run Code Online (Sandbox Code Playgroud)

模板

{% 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 %}
Run Code Online (Sandbox Code Playgroud)

  • 当`articles_set.all` @tatlar应该是`article_set.all`时,你写了`articles_set.all`.有关详细信息,请参阅[以下关系](https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward). (3认同)
  • @Acute-使用[** slice **](https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#slice)模板标签{%在newsletter.article_set中.all | slice:“ 5”%}将获得前五个结果。您将需要确保在视图中使用** order_by **对查询集进行了排序。 (2认同)