在 Django 中迭代相关对象:循环查询集或使用单行 select_related(或 prefetch_related) [英] Iterating over related objects in Django: loop over query set or use one-liner select_related (or prefetch_related)

查看:24
本文介绍了在 Django 中迭代相关对象:循环查询集或使用单行 select_related(或 prefetch_related)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个时事通讯应用程序,其中一个时事通讯在每个问题中都有多篇文章.我想在线显示一个摘要页面,其中列出了时事通讯的年份、数量和标签,然后在一个无序列表中显示该问题的所有文章.我对 Django 很陌生,所以我正在尝试确定最好的方法.

I have a newsletter application where a newsletter has multiple articles within each issue. I want to display a summary page online that lists the newsletter year, volume and label, and then in an unordered list display all the articles in the issue. I am quite new to Django so I am trying to determine the best way to do this.

我定义了模型(只是相关部分):

I have the models defined (just the relevant parts):

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)

我想在网上看到的样子:

What I want to see on the web looks like:

<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>

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

Pretty simple. However, I am confused by the best way to write my view. Whether to use:

  • zip() 然后在模板中迭代的两个列表
  • 使用 select_related() 查询集
  • 使用 prefetch_related() 查询集
  • Two lists which I zip() and then iterate over in the template
  • Use the select_related() queryset
  • Use the prefetch_related() queryset

我使用第一个选项让它工作:

I have it working using the first option:

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})

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

And then render it using the following template:

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 而言是否完全没有效率.如果有更快的方法,我希望不必即时创建列表然后 zip() 将两个列表放在一起.

Pretty straightforward, but as I am pretty new to Django I was wondering if what I am doing is completely inefficient in terms of its powerful ORM. I would love to not have to make a list on-the-fly and then zip() the two lists together if there is a faster way.

TIA.

推荐答案

您现在采用的方法将严重低效,因为它会导致 1+N 个查询.也就是说,1 表示对所有时事通讯的查询,然后每次评估那些 n.article_set.all() 结果时为 1.因此,如果您在第一个查询中有 100 个 Newletter 对象,那么您将执行 101 个查询.

The approach you are doing now will be heavily inefficient, because it will result in an 1+N number of queries. That is, 1 for the query of all your Newsletters, and then 1 for every single time you evaluate those n.article_set.all() results. So if you have 100 Newletter objects in that first query, you will be doing 101 queries.

这是使用 prefetch_related 的绝佳理由.它只会导致 2 个查询.一键获取时事通讯,一键批量获取相关文章.尽管您仍然完全能够继续使用 zip 来组织它们,但是它们已经被缓存了,所以实际上您可以直接将查询传递给模板并在其上循环.:

This is an excellent reason to use prefetch_related. It will only result in 2 queries. One to get the Newsletters, and 1 to batch get the related Articles. Though you are still perfectly able to keep doing the zip to organize them, they will already be cached, so really you can just pass the query directly to the template and loop on that. :

查看

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 %}

这篇关于在 Django 中迭代相关对象:循环查询集或使用单行 select_related(或 prefetch_related)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆