{"title":"聚合","version":"2.2","locale":"zh-hans","docname":"topics/db/aggregation","url":"/zh-hans/2.2/topics/db/aggregation/","canonical":"https://djangodocs.dev/zh-hans/2.2/topics/db/aggregation/","summary":"Django 数据库抽象 API 描述了使用 Django queries 来增删查改单个对象的方法。然而，有时候你要获取的值需要根据一组对象聚合后才能得到。","html":"<h1>聚合<a class=\"heading-anchor\" href=\"#aggregation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p><span class=\"xref std std-doc\">Django 数据库抽象 API</span> 描述了使用 Django queries 来增删查改单个对象的方法。 然而，有时候你要获取的值需要根据一组对象聚合后才能得到。这个主题指南描述了如何使用 Django queries 来生成和返回聚合值的方法。</p>\n<p>整篇指南我们将引用以下模型。这些模型用来记录多个网上书店的库存。</p>\n<div class=\"code-block\" data-language=\"python\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> models\n\n<span class=\"k\">class</span> <span class=\"nc\">Author</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">100</span><span class=\"p\">)</span>\n    age <span class=\"o\">=</span> models<span class=\"o\">.</span>IntegerField<span class=\"p\">()</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Publisher</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">300</span><span class=\"p\">)</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Book</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">300</span><span class=\"p\">)</span>\n    pages <span class=\"o\">=</span> models<span class=\"o\">.</span>IntegerField<span class=\"p\">()</span>\n    price <span class=\"o\">=</span> models<span class=\"o\">.</span>DecimalField<span class=\"p\">(</span>max_digits<span class=\"o\">=</span><span class=\"mi\">10</span><span class=\"p\">,</span> decimal_places<span class=\"o\">=</span><span class=\"mi\">2</span><span class=\"p\">)</span>\n    rating <span class=\"o\">=</span> models<span class=\"o\">.</span>FloatField<span class=\"p\">()</span>\n    authors <span class=\"o\">=</span> models<span class=\"o\">.</span>ManyToManyField<span class=\"p\">(</span>Author<span class=\"p\">)</span>\n    publisher <span class=\"o\">=</span> models<span class=\"o\">.</span>ForeignKey<span class=\"p\">(</span>Publisher<span class=\"p\">,</span> on_delete<span class=\"o\">=</span>models<span class=\"o\">.</span>CASCADE<span class=\"p\">)</span>\n    pubdate <span class=\"o\">=</span> models<span class=\"o\">.</span>DateField<span class=\"p\">()</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Store</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">300</span><span class=\"p\">)</span>\n    books <span class=\"o\">=</span> models<span class=\"o\">.</span>ManyToManyField<span class=\"p\">(</span>Book<span class=\"p\">)</span>\n</code></pre></div>\n<section id=\"cheat-sheet\">\n<h2>速查表<a class=\"heading-anchor\" href=\"#cheat-sheet\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>下面是根据以上模型执行常见的聚合查询：</p>\n<div class=\"code-block\" data-language=\"python\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"c1\"># Total number of books.</span>\n<span class=\"o\">&gt;&gt;&gt;</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>count<span class=\"p\">()</span>\n<span class=\"mi\">2452</span>\n\n<span class=\"c1\"># Total number of books with publisher=BaloneyPress</span>\n<span class=\"o\">&gt;&gt;&gt;</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>publisher__name<span class=\"o\">=</span><span class=\"s1\">&#39;BaloneyPress&#39;</span><span class=\"p\">)</span><span class=\"o\">.</span>count<span class=\"p\">()</span>\n<span class=\"mi\">73</span>\n\n<span class=\"c1\"># Average price across all books.</span>\n<span class=\"o\">&gt;&gt;&gt;</span> <span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Avg\n<span class=\"o\">&gt;&gt;&gt;</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>all<span class=\"p\">()</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"p\">{</span><span class=\"s1\">&#39;price__avg&#39;</span><span class=\"p\">:</span> <span class=\"mf\">34.35</span><span class=\"p\">}</span>\n\n<span class=\"c1\"># Max price across all books.</span>\n<span class=\"o\">&gt;&gt;&gt;</span> <span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Max\n<span class=\"o\">&gt;&gt;&gt;</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>all<span class=\"p\">()</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Max<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"p\">{</span><span class=\"s1\">&#39;price__max&#39;</span><span class=\"p\">:</span> Decimal<span class=\"p\">(</span><span class=\"s1\">&#39;81.20&#39;</span><span class=\"p\">)}</span>\n\n<span class=\"c1\"># Difference between the highest priced book and the average price of all books.</span>\n<span class=\"o\">&gt;&gt;&gt;</span> <span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> FloatField\n<span class=\"o\">&gt;&gt;&gt;</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>\n<span class=\"o\">...</span>     price_diff<span class=\"o\">=</span>Max<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">,</span> output_field<span class=\"o\">=</span>FloatField<span class=\"p\">())</span> <span class=\"o\">-</span> Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"p\">{</span><span class=\"s1\">&#39;price_diff&#39;</span><span class=\"p\">:</span> <span class=\"mf\">46.85</span><span class=\"p\">}</span>\n\n<span class=\"c1\"># All the following queries involve traversing the Book&lt;-&gt;Publisher</span>\n<span class=\"c1\"># foreign key relationship backwards.</span>\n\n<span class=\"c1\"># Each publisher, each with a count of books as a &quot;num_books&quot; attribute.</span>\n<span class=\"o\">&gt;&gt;&gt;</span> <span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Count\n<span class=\"o\">&gt;&gt;&gt;</span> pubs <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_books<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">))</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs\n<span class=\"o\">&lt;</span>QuerySet <span class=\"p\">[</span><span class=\"o\">&lt;</span>Publisher<span class=\"p\">:</span> BaloneyPress<span class=\"o\">&gt;</span><span class=\"p\">,</span> <span class=\"o\">&lt;</span>Publisher<span class=\"p\">:</span> SalamiPress<span class=\"o\">&gt;</span><span class=\"p\">,</span> <span class=\"o\">...</span><span class=\"p\">]</span><span class=\"o\">&gt;</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>num_books\n<span class=\"mi\">73</span>\n\n<span class=\"c1\"># Each publisher, with a separate count of books with a rating above and below 5</span>\n<span class=\"o\">&gt;&gt;&gt;</span> <span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Q\n<span class=\"o\">&gt;&gt;&gt;</span> above_5 <span class=\"o\">=</span> Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">,</span> <span class=\"nb\">filter</span><span class=\"o\">=</span>Q<span class=\"p\">(</span>book__rating__gt<span class=\"o\">=</span><span class=\"mi\">5</span><span class=\"p\">))</span>\n<span class=\"o\">&gt;&gt;&gt;</span> below_5 <span class=\"o\">=</span> Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">,</span> <span class=\"nb\">filter</span><span class=\"o\">=</span>Q<span class=\"p\">(</span>book__rating__lte<span class=\"o\">=</span><span class=\"mi\">5</span><span class=\"p\">))</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>below_5<span class=\"o\">=</span>below_5<span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>above_5<span class=\"o\">=</span>above_5<span class=\"p\">)</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>above_5\n<span class=\"mi\">23</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>below_5\n<span class=\"mi\">12</span>\n\n<span class=\"c1\"># The top 5 publishers, in order by number of books.</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_books<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">))</span><span class=\"o\">.</span>order_by<span class=\"p\">(</span><span class=\"s1\">&#39;-num_books&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n<span class=\"o\">&gt;&gt;&gt;</span> pubs<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>num_books\n<span class=\"mi\">1323</span>\n</code></pre></div>\n</section>\n<section id=\"generating-aggregates-over-a-queryset\">\n<h2>在 <code class=\"docutils literal notranslate\">QuerySet</code> 上生成聚合<a class=\"heading-anchor\" href=\"#generating-aggregates-over-a-queryset\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Django 提供了两种生成聚合的方法。第一种方法是从整个 <code class=\"docutils literal notranslate\">QuerySet</code> 生成汇总值。比如你想要计算所有在售书的平均价格。Django 的查询语法提供了一种用来描述所有图书集合的方法：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>all<span class=\"p\">()</span>\n</code></pre></div>\n<p>可以通过在 <code class=\"docutils literal notranslate\">QuerySet</code> 后添加 <code class=\"docutils literal notranslate\">aggregate()</code> 子句来计算 <code class=\"docutils literal notranslate\">QuerySet</code> 对象的汇总值。</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Avg\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>all<span class=\"p\">()</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;price__avg&#39;: 34.35}</span>\n</code></pre></div>\n<p>本例中的 <code class=\"docutils literal notranslate\">all()</code> 是多余的，所以可以简化成这样的:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;price__avg&#39;: 34.35}</span>\n</code></pre></div>\n<p>传递给 <code class=\"docutils literal notranslate\">aggregate()</code> 的参数描述了我们想要计算的聚合值。在这个例子里，要计算的就是 <code class=\"docutils literal notranslate\">Book</code> 模型上的 <code class=\"docutils literal notranslate\">price</code> 字段的平均值。可用的聚合函数列表可以在 <a class=\"reference internal\" href=\"/zh-hans/2.2/ref/models/querysets/#aggregation-functions\"><span class=\"std std-ref\">QuerySet reference</span></a> 中找到。</p>\n<p><code class=\"docutils literal notranslate\">aggregate()</code> 是 <code class=\"docutils literal notranslate\">QuerySet</code> 的一个终端子句，使用后将返回“名称-值”的字典，其中“名称”就是聚合值的标志，“值”就是计算出的聚合结果。“名称”是根据字段名和聚合函数而自动生成的。如果你想指定一个聚合值的名称，你可以在指定聚合子句的时候提供指定的名称：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>average_price<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;average_price&#39;: 34.35}</span>\n</code></pre></div>\n<p>如果你想生成更多的聚合内容，你只需要在 <code class=\"docutils literal notranslate\">aggregate()</code> 子句中加入其它参数即可。所以，如果我们也想知道所有书中最高和最低的价格，我们可以写这样的查询：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Avg<span class=\"p\">,</span> Max<span class=\"p\">,</span> Min\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">),</span> Max<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">),</span> Min<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;price__avg&#39;: 34.35, &#39;price__max&#39;: Decimal(&#39;81.20&#39;), &#39;price__min&#39;: Decimal(&#39;12.99&#39;)}</span>\n</code></pre></div>\n</section>\n<section id=\"generating-aggregates-for-each-item-in-a-queryset\">\n<h2>为 <code class=\"docutils literal notranslate\">QuerySet</code> 中的每一个条目生成聚合<a class=\"heading-anchor\" href=\"#generating-aggregates-for-each-item-in-a-queryset\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>生成值的汇总的另一个办法是为 <a class=\"reference internal\" href=\"/zh-hans/2.2/ref/models/querysets/#django.db.models.query.QuerySet\" title=\"django.db.models.query.QuerySet\"><code class=\"xref py py-class docutils literal notranslate\">QuerySet</code></a> 的每一个对象生成独立汇总。比如，如果你想检索书籍列表，你可能想知道每一本书有多少作者。每一本书与作者有多对多的关系；我们想在 <code class=\"docutils literal notranslate\">QuerySet</code> 中为每一本书总结这个关系。</p>\n<p>使用 <a class=\"reference internal\" href=\"/zh-hans/2.2/ref/models/querysets/#django.db.models.query.QuerySet.annotate\" title=\"django.db.models.query.QuerySet.annotate\"><code class=\"xref py py-meth docutils literal notranslate\">annotate()</code></a> 子句可以生成每一个对象的汇总。当指定  <code class=\"docutils literal notranslate\">annotate()</code> 子句，<code class=\"docutils literal notranslate\">QuerySet</code> 中的每一个对象将对指定值进行汇总。</p>\n<p>这些汇总语法规则与 <a class=\"reference internal\" href=\"/zh-hans/2.2/ref/models/querysets/#django.db.models.query.QuerySet.aggregate\" title=\"django.db.models.query.QuerySet.aggregate\"><code class=\"xref py py-meth docutils literal notranslate\">aggregate()</code></a> 子句的规则相同。<code class=\"docutils literal notranslate\">annotate()</code> 的每一个参数描述了一个要计算的聚合。比如，注解（annotate）所有书的所有作者：</p>\n<div class=\"code-block\" data-language=\"python\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"c1\"># Build an annotated queryset</span>\n<span class=\"o\">&gt;&gt;&gt;</span> <span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Count\n<span class=\"o\">&gt;&gt;&gt;</span> q <span class=\"o\">=</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">))</span>\n<span class=\"c1\"># Interrogate the first object in the queryset</span>\n<span class=\"o\">&gt;&gt;&gt;</span> q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n<span class=\"o\">&lt;</span>Book<span class=\"p\">:</span> The Definitive Guide to Django<span class=\"o\">&gt;</span>\n<span class=\"o\">&gt;&gt;&gt;</span> q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>authors__count\n<span class=\"mi\">2</span>\n<span class=\"c1\"># Interrogate the second object in the queryset</span>\n<span class=\"o\">&gt;&gt;&gt;</span> q<span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n<span class=\"o\">&lt;</span>Book<span class=\"p\">:</span> Practical Django Projects<span class=\"o\">&gt;</span>\n<span class=\"o\">&gt;&gt;&gt;</span> q<span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">]</span><span class=\"o\">.</span>authors__count\n<span class=\"mi\">1</span>\n</code></pre></div>\n<p>与 <code class=\"docutils literal notranslate\">aggregate()</code> 一样，注解的名称是根据聚合函数和被聚合的字段名自动生成的。当你在指定注解的时候，你可以通过提供一个别名重写这个默认名：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>q <span class=\"o\">=</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>num_authors\n<span class=\"go\">2</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">]</span><span class=\"o\">.</span>num_authors\n<span class=\"go\">1</span>\n</code></pre></div>\n<p>与 <code class=\"docutils literal notranslate\">aggregate()</code> 不同的是，<code class=\"docutils literal notranslate\">annotate()</code> 不是终端子句。<code class=\"docutils literal notranslate\">annotate()</code> 子句的输出就是 <code class=\"docutils literal notranslate\">QuerySet</code>；这个 <code class=\"docutils literal notranslate\">QuerySet</code> 被其他 <code class=\"docutils literal notranslate\">QuerySet</code> 操作进行修改，包括 filter()`, <code class=\"docutils literal notranslate\">order_by()</code> ，甚至可以对 <code class=\"docutils literal notranslate\">annotate()</code> 进行额外调用。</p>\n<section id=\"combining-multiple-aggregations\">\n<span id=\"id1\"></span><h3>组合多个聚合<a class=\"heading-anchor\" href=\"#combining-multiple-aggregations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>使用 <code class=\"docutils literal notranslate\">annotate()</code> 组合多个聚合将产生错误的结果( <a class=\"extlink-ticket reference external\" href=\"https://code.djangoproject.com/ticket/10060\">yield the wrong results</a> )，因为它使用连接(joins)而不是子查询：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>book <span class=\"o\">=</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>first<span class=\"p\">()</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>book<span class=\"o\">.</span>authors<span class=\"o\">.</span>count<span class=\"p\">()</span>\n<span class=\"go\">2</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>book<span class=\"o\">.</span>store_set<span class=\"o\">.</span>count<span class=\"p\">()</span>\n<span class=\"go\">3</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q <span class=\"o\">=</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">),</span> Count<span class=\"p\">(</span><span class=\"s1\">&#39;store&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>authors__count\n<span class=\"go\">6</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>store__count\n<span class=\"go\">6</span>\n</code></pre></div>\n<p>对大部分聚合来说，没办法避免这个问题，但是，<a class=\"reference internal\" href=\"/zh-hans/2.2/ref/models/querysets/#django.db.models.Count\" title=\"django.db.models.Count\"><code class=\"xref py py-class docutils literal notranslate\">Count</code></a> 聚合可以使用 <code class=\"docutils literal notranslate\">distinct</code> 参数来避免：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>q <span class=\"o\">=</span> Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">,</span> distinct<span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">),</span> Count<span class=\"p\">(</span><span class=\"s1\">&#39;store&#39;</span><span class=\"p\">,</span> distinct<span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>authors__count\n<span class=\"go\">2</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span><span class=\"o\">.</span>store__count\n<span class=\"go\">3</span>\n</code></pre></div>\n<aside class=\"admonition-if-in-doubt-inspect-the-sql-query admonition\" role=\"note\">\n<p class=\"admonition-title\">如有疑问，请检查 SQL 查询！</p>\n<p>为了搞清楚你的查询发生了什么问题，你得在 <code class=\"docutils literal notranslate\">QuerySet</code> 中检查一下``query`` 属性。</p>\n</aside>\n</section>\n</section>\n<section id=\"joins-and-aggregates\">\n<h2>连接(Joins)和聚合<a class=\"heading-anchor\" href=\"#joins-and-aggregates\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>到目前为止，我们已经处理了被查询模型字段的聚合。然而，有时候想聚合的值属于你正在查询模型的关联模型。</p>\n<p>在聚合函数里面指定聚合的字段时，Django 允许你在过滤相关字段的时候使用相同的双下划线表示法。Django 将处理任何需要检索和聚合的关联值的表连接(table joins)。</p>\n<p>比如，要寻找每个书店提供的书籍价格区间，你可以使用这个注解(annotation)：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Max<span class=\"p\">,</span> Min\n<span class=\"gp\">&gt;&gt;&gt; </span>Store<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>min_price<span class=\"o\">=</span>Min<span class=\"p\">(</span><span class=\"s1\">&#39;books__price&#39;</span><span class=\"p\">),</span> max_price<span class=\"o\">=</span>Max<span class=\"p\">(</span><span class=\"s1\">&#39;books__price&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>这告诉 Django 去检索 <code class=\"docutils literal notranslate\">Store</code> 模型，连接（通过多对多关系） <code class=\"docutils literal notranslate\">Book</code> 模型，并且聚合书籍模型的价格字段来获取最大最小值。</p>\n<p>相同规则应用于 <code class=\"docutils literal notranslate\">aggregate()</code> 从句。如果你想知道任何店铺正在销售的任何书籍的最低最高价，你可以使用这个聚合：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Store<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>min_price<span class=\"o\">=</span>Min<span class=\"p\">(</span><span class=\"s1\">&#39;books__price&#39;</span><span class=\"p\">),</span> max_price<span class=\"o\">=</span>Max<span class=\"p\">(</span><span class=\"s1\">&#39;books__price&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>Join 链可以根据你的需求尽可能深。比如，要提取所出售的书籍中最年轻的作者年龄，你可以写这样的查询：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Store<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>youngest_age<span class=\"o\">=</span>Min<span class=\"p\">(</span><span class=\"s1\">&#39;books__authors__age&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<section id=\"following-relationships-backwards\">\n<h3>反向关系<a class=\"heading-anchor\" href=\"#following-relationships-backwards\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>类似于 <a class=\"reference internal\" href=\"/zh-hans/2.2/topics/db/queries/#lookups-that-span-relationships\"><span class=\"std std-ref\">跨关系查询</span></a> ，你正在查询的在模型和模型字段上的聚合和注解(annotations)可以包含反向关系。关系模型的小写名和双下划线也可以用在这里。</p>\n<p>比如我们可以要求所有出版者，注解他们各自的图书库存总数（注意我们如何使用 <code class=\"docutils literal notranslate\">'book'</code> 来指定 <code class=\"docutils literal notranslate\">Publisher</code> -&gt; <code class=\"docutils literal notranslate\">Book</code> 反向外键跳转）：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Avg<span class=\"p\">,</span> Count<span class=\"p\">,</span> Min<span class=\"p\">,</span> Sum\n<span class=\"gp\">&gt;&gt;&gt; </span>Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>(查询结果里的每一个 <code class=\"docutils literal notranslate\">Publisher</code> 会有多余的属性—— <code class=\"docutils literal notranslate\">book__count</code> 。)</p>\n<p>我们也可以找出最古老的一本：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>oldest_pubdate<span class=\"o\">=</span>Min<span class=\"p\">(</span><span class=\"s1\">&#39;book__pubdate&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>(结果字典中会有一个叫 <code class=\"docutils literal notranslate\">'oldest_pubdate'</code> 的键。如果没有指定这样的别名，它将会是一个很长的名字 <code class=\"docutils literal notranslate\">'book__pubdate__min'</code> 。)</p>\n<p>它不仅仅用于外键，它也适用于多对多关系。比如，我们能查询每一个作者，注解作者(共同)创作的书籍总页数(注意我们如何使用 <code class=\"docutils literal notranslate\">'book'</code> 来指定 <code class=\"docutils literal notranslate\">Author</code> -&gt; <code class=\"docutils literal notranslate\">Book</code> 反向多对多跳转)：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Author<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>total_pages<span class=\"o\">=</span>Sum<span class=\"p\">(</span><span class=\"s1\">&#39;book__pages&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>（结果集里的每一个 <code class=\"docutils literal notranslate\">Author</code> 会有一个额外的属性——<code class=\"docutils literal notranslate\">total_pages</code>）如果没有指定这样的别名，它将会是一个很长的名字 <code class=\"docutils literal notranslate\">book__pages__sum</code>）</p>\n<p>或查询书籍的平均评分：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Author<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>average_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;book__rating&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>（结果字典会有一个叫 <code class=\"docutils literal notranslate\">'average_rating'</code> 的键。如果没有指定这样的别名，它将会是一个很长的名字 <code class=\"docutils literal notranslate\">'book__rating__avg'</code>。）</p>\n</section>\n</section>\n<section id=\"aggregations-and-other-queryset-clauses\">\n<h2>聚合和其他 <code class=\"docutils literal notranslate\">QuerySet</code> 子句<a class=\"heading-anchor\" href=\"#aggregations-and-other-queryset-clauses\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<section id=\"filter-and-exclude\">\n<h3><code class=\"docutils literal notranslate\">filter()</code> 和 <code class=\"docutils literal notranslate\">exclude()</code><a class=\"heading-anchor\" href=\"#filter-and-exclude\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>聚合也可以参与过滤。任何应用于普通模型字段的 <code class=\"docutils literal notranslate\">filter()</code> （或 <code class=\"docutils literal notranslate\">exclude()</code>）会具有约束被认为是聚合的对象的效果。</p>\n<p>当使用 <code class=\"docutils literal notranslate\">annotate()</code> 从句，过滤器具有约束计算注解的对象的效果。比如，你可以使用查询生成一个所有书籍的注解列表，这个列表的标题以 &quot;Django&quot; 开头。</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Avg<span class=\"p\">,</span> Count\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>name__startswith<span class=\"o\">=</span><span class=\"s2\">&quot;Django&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>当使用 <code class=\"docutils literal notranslate\">aggregate()</code> 子句，过滤器将具有约束计算聚合的对象的效果。比如，你可以使用查询生成所有标题以 &quot;Django&quot; 开头的平均价格。</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>name__startswith<span class=\"o\">=</span><span class=\"s2\">&quot;Django&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;price&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<section id=\"filtering-on-annotations\">\n<span id=\"id2\"></span><h4>过滤注解<a class=\"heading-anchor\" href=\"#filtering-on-annotations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>注解过的值也可以使用过滤器。注解的别名可以和任何其他模型字段一样使用 <code class=\"docutils literal notranslate\">filter()</code> 和 <code class=\"docutils literal notranslate\">exclude()</code> 子句。</p>\n<p>比如，要生成多名作者的书籍列表，可以发出这种查询：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">))</span><span class=\"o\">.</span>filter<span class=\"p\">(</span>num_authors__gt<span class=\"o\">=</span><span class=\"mi\">1</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>这个查询生成一个注解结果集，然后生成一个基于注解的过滤器。</p>\n<p>如果你需要两个带有两个独立的过滤器的注解，你可以在任何聚合中使用 <code class=\"docutils literal notranslate\">filter</code> 语句。比如，要生成一个带有高评价书籍的作者列表：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>highly_rated <span class=\"o\">=</span> Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">,</span> <span class=\"nb\">filter</span><span class=\"o\">=</span>Q<span class=\"p\">(</span>book__rating__gte<span class=\"o\">=</span><span class=\"mi\">7</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>Author<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_books<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">),</span> highly_rated_books<span class=\"o\">=</span>highly_rated<span class=\"p\">)</span>\n</code></pre></div>\n<p>结果集中的每一个``Author`` 会有 <code class=\"docutils literal notranslate\">num_books</code> 和 <code class=\"docutils literal notranslate\">highly_rated_books</code> 属性。</p>\n<aside class=\"admonition-choosing-between-filter-and-queryset-filter admonition\" role=\"note\">\n<p class=\"admonition-title\">在 <code class=\"docutils literal notranslate\">filter</code> 和 <code class=\"docutils literal notranslate\">QuerySet.filter()</code> 中做选择</p>\n<p>避免在单个注解和聚合中使用 <code class=\"docutils literal notranslate\">filter</code> 语句。使用 <code class=\"docutils literal notranslate\">QuerySet.filter()</code> 来排除列会很高效。聚合 <code class=\"docutils literal notranslate\">filter</code> 语句只在使用具有不同条件的相同关系的两个或以上的聚合时有用。</p>\n</aside>\n</section>\n<section id=\"order-of-annotate-and-filter-clauses\">\n<h4><code class=\"docutils literal notranslate\">annotate()</code> 和 <code class=\"docutils literal notranslate\">filter()</code> 子句的顺序<a class=\"heading-anchor\" href=\"#order-of-annotate-and-filter-clauses\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>当开发一个涉及 <code class=\"docutils literal notranslate\">annotate()</code> 和 <code class=\"docutils literal notranslate\">filter()</code> 子句的复杂查询时，要特别注意应用于  <code class=\"docutils literal notranslate\">QuerySet</code> 的子句的顺序。</p>\n<p>当一个 <code class=\"docutils literal notranslate\">annotate()</code> 子句应用于查询，会根据查询状态来计算注解，直到请求的注解为止。这实际上意味着 <code class=\"docutils literal notranslate\">filter()</code> 和 <code class=\"docutils literal notranslate\">annotate()</code> 不是可交换的操作。</p>\n<p>比如：</p>\n<ul class=\"simple\">\n<li><p>出版者A有两本评分4和5的书。</p></li>\n<li><p>出版者B有两本评分1和4的书。</p></li>\n<li><p>出版者C有一本评分1的书。</p></li>\n</ul>\n<p>下面就是 <code class=\"docutils literal notranslate\">Count</code> 聚合的例子：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> b <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_books<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">,</span> distinct<span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">))</span><span class=\"o\">.</span>filter<span class=\"p\">(</span>book__rating__gt<span class=\"o\">=</span><span class=\"mf\">3.0</span><span class=\"p\">)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> a<span class=\"o\">.</span>num_books\n<span class=\"go\">(&lt;Publisher: A&gt;, 2)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>b<span class=\"p\">,</span> b<span class=\"o\">.</span>num_books\n<span class=\"go\">(&lt;Publisher: B&gt;, 2)</span>\n\n<span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> b <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>book__rating__gt<span class=\"o\">=</span><span class=\"mf\">3.0</span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_books<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;book&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> a<span class=\"o\">.</span>num_books\n<span class=\"go\">(&lt;Publisher: A&gt;, 2)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>b<span class=\"p\">,</span> b<span class=\"o\">.</span>num_books\n<span class=\"go\">(&lt;Publisher: B&gt;, 1)</span>\n</code></pre></div>\n<p>两个查询返回出版者列表，这些出版者至少有一本评分3的书，因此排除了C。</p>\n<p>在第一个查询里，注解优先于过滤器，因此过滤器没有影响注解。<code class=\"docutils literal notranslate\">distinct=True</code> 用来避免 a <a class=\"reference internal\" href=\"#combining-multiple-aggregations\"><span class=\"std std-ref\">query bug</span></a>。</p>\n<p>第二个查询每个发布者评分3以上的书籍数量。过滤器优先于注解，因此过滤器约束计算注解时考虑的对象。</p>\n<p>这里是另一个关于 <code class=\"docutils literal notranslate\">Avg</code> 聚合的例子：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> b <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>avg_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;book__rating&#39;</span><span class=\"p\">))</span><span class=\"o\">.</span>filter<span class=\"p\">(</span>book__rating__gt<span class=\"o\">=</span><span class=\"mf\">3.0</span><span class=\"p\">)</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> a<span class=\"o\">.</span>avg_rating\n<span class=\"go\">(&lt;Publisher: A&gt;, 4.5)  # (5+4)/2</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>b<span class=\"p\">,</span> b<span class=\"o\">.</span>avg_rating\n<span class=\"go\">(&lt;Publisher: B&gt;, 2.5)  # (1+4)/2</span>\n\n<span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> b <span class=\"o\">=</span> Publisher<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>book__rating__gt<span class=\"o\">=</span><span class=\"mf\">3.0</span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>avg_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;book__rating&#39;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>a<span class=\"p\">,</span> a<span class=\"o\">.</span>avg_rating\n<span class=\"go\">(&lt;Publisher: A&gt;, 4.5)  # (5+4)/2</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>b<span class=\"p\">,</span> b<span class=\"o\">.</span>avg_rating\n<span class=\"go\">(&lt;Publisher: B&gt;, 4.0)  # 4/1 (book with rating 1 excluded)</span>\n</code></pre></div>\n<p>第一个查询请求至少有一本评分3以上的书籍的出版者的书籍平均分。第二个查询只请求评分3以上的作者书籍的平均评分。</p>\n<p>很难凭直觉了解ORM如何将复杂的查询集转化为SQL查询，因此当有疑问时，请使用 str(queryset.query)` 检查SQL，并写大量的测试。</p>\n</section>\n</section>\n<section id=\"order-by\">\n<h3><code class=\"docutils literal notranslate\">order_by()</code><a class=\"heading-anchor\" href=\"#order-by\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>注解可以当做基本排序来使用。当你定义了一个 <code class=\"docutils literal notranslate\">order_by()</code> 子句，你提供的聚合可以引用任何定义为查询中 <code class=\"docutils literal notranslate\">annotate()</code> 子句的一部分的别名。</p>\n<p>比如，通过书籍的作者数量来对书籍的 <code class=\"docutils literal notranslate\">QuerySet</code> 排序，你可以使用下面的查询：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">))</span><span class=\"o\">.</span>order_by<span class=\"p\">(</span><span class=\"s1\">&#39;num_authors&#39;</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n<section id=\"values\">\n<h3><code class=\"docutils literal notranslate\">values()</code><a class=\"heading-anchor\" href=\"#values\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>通常，注解值会添加到每个对象上，即一个被注解的 <code class=\"docutils literal notranslate\">QuerySet</code> 将会为初始 <code class=\"docutils literal notranslate\">QuerySet</code> 的每个对象返回一个结果集。然而，当使用 <code class=\"docutils literal notranslate\">values()</code> 子句来对结果集进行约束时，生成注解值的方法会稍有不同。不是在原始 <code class=\"docutils literal notranslate\">QuerySet</code> 中对每个对象添加注解并返回，而是根据定义在 <code class=\"docutils literal notranslate\">values()</code> 子句中的字段组合先对结果进行分组，再对每个单独的分组进行注解，这个注解值是根据分组中所有的对象计算得到的。</p>\n<p>下面是一个关于作者的查询例子，查询每个作者所著书的平均评分：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Author<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>average_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;book__rating&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>这段代码返回的是数据库中的所有作者及其所著书的平均评分。</p>\n<p>但是如果你使用 <code class=\"docutils literal notranslate\">values()</code> 子句，结果会稍有不同：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Author<span class=\"o\">.</span>objects<span class=\"o\">.</span>values<span class=\"p\">(</span><span class=\"s1\">&#39;name&#39;</span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>average_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;book__rating&#39;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>在这个例子中，作者会按名字分组，所以你只能得到不重名的作者分组的注解值。这意味着如果你有两个作者同名，那么他们原本各自的查询结果将被合并到同一个结果中；两个作者的所有评分都将被计算为一个平均分。</p>\n<section id=\"order-of-annotate-and-values-clauses\">\n<h4><code class=\"docutils literal notranslate\">annotate()</code> 和 <code class=\"docutils literal notranslate\">values()</code> 的顺序<a class=\"heading-anchor\" href=\"#order-of-annotate-and-values-clauses\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>和使用 <code class=\"docutils literal notranslate\">filter()</code> 一样，作用于某个查询的 <code class=\"docutils literal notranslate\">annotate()</code> 和 <code class=\"docutils literal notranslate\">values()</code> 子句的顺序非常重要。如果 <code class=\"docutils literal notranslate\">values()</code> 子句在 <code class=\"docutils literal notranslate\">annotate()</code> 之前，就会根据 <code class=\"docutils literal notranslate\">values()</code> 子句产生的分组来计算注解。</p>\n<p>然而如果 <code class=\"docutils literal notranslate\">annotate()</code> 子句在 <code class=\"docutils literal notranslate\">values()</code> 之前，就会根据整个查询集生成注解。这种情况下，<code class=\"docutils literal notranslate\">values()</code> 子句只能限制输出的字段。</p>\n<p>举个例子，如果我们颠倒上个例子中 <code class=\"docutils literal notranslate\">values()</code> 和 <code class=\"docutils literal notranslate\">annotate()</code> 的顺序：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>Author<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>average_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;book__rating&#39;</span><span class=\"p\">))</span><span class=\"o\">.</span>values<span class=\"p\">(</span><span class=\"s1\">&#39;name&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;average_rating&#39;</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>这段代码将为每个作者添加一个唯一注解，但只有作者姓名和 <code class=\"docutils literal notranslate\">average_rating</code> 注解会返回在输出结果中。</p>\n<p>你应该也会注意 <code class=\"docutils literal notranslate\">average_rating</code> 已经明确包含在返回的值列表中。这是必需的，因为 <code class=\"docutils literal notranslate\">values()</code> 和 <code class=\"docutils literal notranslate\">annotate()</code> 子句的顺序。</p>\n<p>如果 <code class=\"docutils literal notranslate\">values()</code> 子句在 <code class=\"docutils literal notranslate\">annotate()</code> 子句之前，任何注解将自动添加在结果集中。然而，如果 <code class=\"docutils literal notranslate\">values()</code> 子句应用在 <code class=\"docutils literal notranslate\">annotate()</code> 子句之后，则需要显式包含聚合列。</p>\n</section>\n<section id=\"interaction-with-default-ordering-or-order-by\">\n<span id=\"aggregation-ordering-interaction\"></span><h4>与默认排序或 <code class=\"docutils literal notranslate\">order_by()</code> 交互<a class=\"heading-anchor\" href=\"#interaction-with-default-ordering-or-order-by\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<aside class=\"version-note version-deprecated\" data-version=\"2.2\" role=\"note\">\n<p class=\"version-note-title\">Deprecated since Django 2.2</p><p><span class=\"versionmodified deprecated\">Deprecated since version 2.2: </span>从Django 3.1 中开始，模型的 <code class=\"docutils literal notranslate\">Meta.ordering</code> 中的排序不会使用在 <code class=\"docutils literal notranslate\">GROUP BY</code> 查询，比如  <code class=\"docutils literal notranslate\">.annotate().values()</code> 。从 Django 2.2 开始，这些查询发出一个弃用警告，指示要在查询集中添加一个显式的 <code class=\"docutils literal notranslate\">order_by()</code> 来静默警告。</p>\n</aside>\n<p>在选择输出数据的时候，将使用在查询集的 <code class=\"docutils literal notranslate\">order_by()</code> 中提及的字段（或模型上的默认排序中使用的字段），即使它们不是其他在 <code class=\"docutils literal notranslate\">values()</code> 中指定的调用。这些额外的字段用于将“相似”的结果分组在一起，它们可以使其他相同的结果行看起来是分开的。尤其是在数数的时候。</p>\n<p>举个例子，假设你有这样的模型：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span> <span class=\"nn\">django.db</span> <span class=\"kn\">import</span> models\n\n<span class=\"k\">class</span> <span class=\"nc\">Item</span><span class=\"p\">(</span>models<span class=\"o\">.</span>Model<span class=\"p\">):</span>\n    name <span class=\"o\">=</span> models<span class=\"o\">.</span>CharField<span class=\"p\">(</span>max_length<span class=\"o\">=</span><span class=\"mi\">10</span><span class=\"p\">)</span>\n    data <span class=\"o\">=</span> models<span class=\"o\">.</span>IntegerField<span class=\"p\">()</span>\n\n    <span class=\"k\">class</span> <span class=\"nc\">Meta</span><span class=\"p\">:</span>\n        ordering <span class=\"o\">=</span> <span class=\"p\">[</span><span class=\"s2\">&quot;name&quot;</span><span class=\"p\">]</span>\n</code></pre></div>\n<p>这里的重点是在 <code class=\"docutils literal notranslate\">name</code> 字段上的默认排序。如果你想计算每一个不同的 <code class=\"docutils literal notranslate\">data</code> 值出现次数，你可以这样做：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"c1\"># Warning: not quite correct!</span>\nItem<span class=\"o\">.</span>objects<span class=\"o\">.</span>values<span class=\"p\">(</span><span class=\"s2\">&quot;data&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>Count<span class=\"p\">(</span><span class=\"s2\">&quot;id&quot;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>它将通过共同的 <code class=\"docutils literal notranslate\">data</code> 值对 <code class=\"docutils literal notranslate\">Item</code> 对象进行分组，然后在每一组里计算 <code class=\"docutils literal notranslate\">id</code> 值的数量。只可惜它不能正常工作。通过 <code class=\"docutils literal notranslate\">name</code> 进行的默认排序也在分组中起作用，因此这个查询将按不同的 <code class=\"docutils literal notranslate\">(data, name)</code> 分组，这不是你想要的。相反，你可以构建这样的查询集：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>Item<span class=\"o\">.</span>objects<span class=\"o\">.</span>values<span class=\"p\">(</span><span class=\"s2\">&quot;data&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>Count<span class=\"p\">(</span><span class=\"s2\">&quot;id&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span>order_by<span class=\"p\">()</span>\n</code></pre></div>\n<p>清除任何查询中的排序。你也可以通过 <code class=\"docutils literal notranslate\">data</code> 排序，没有任何有害影响，因为它已经在查询中发挥了作用。</p>\n<p>这个行为与 <a class=\"reference internal\" href=\"/zh-hans/2.2/ref/models/querysets/#django.db.models.query.QuerySet.distinct\" title=\"django.db.models.query.QuerySet.distinct\"><code class=\"xref py py-meth docutils literal notranslate\">distinct()</code></a> 的查询文档指出的行为相同，一般规则是一样的：通常情况下，你不希望额外的列在结果中发挥作用，因此要清除排序，或者至少确保它只限于您在 <code class=\"docutils literal notranslate\">values()</code> 调用中选择的那些字段。</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>你可以会问为什么 Django 没有移除无关的列。主要原因就是与 <code class=\"docutils literal notranslate\">distinct()</code> 和其他地方的一致性:Django从不删除你指定的排序约束（我们不能改变其他方法的行为，因为这会违反我们的应用编程接口的稳定性政策）。</p>\n</aside>\n</section>\n</section>\n<section id=\"aggregating-annotations\">\n<h3>聚合注解<a class=\"heading-anchor\" href=\"#aggregating-annotations\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>你也可以在注解结果上生成聚合。当你定义 <code class=\"docutils literal notranslate\">aggregate()</code> 子句时，你提供的聚合可以引用任何定义在查询中 <code class=\"docutils literal notranslate\">annotate()</code> 子句的别名。</p>\n<p>比如，如果你想计算每本书的平均作者数，首先使用作者数注解书籍集合，然后引用注解字段聚合作者数：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"gp\">&gt;&gt;&gt; </span><span class=\"kn\">from</span> <span class=\"nn\">django.db.models</span> <span class=\"kn\">import</span> Avg<span class=\"p\">,</span> Count\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>annotate<span class=\"p\">(</span>num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s1\">&#39;authors&#39;</span><span class=\"p\">))</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s1\">&#39;num_authors&#39;</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;num_authors__avg&#39;: 1.66}</span>\n</code></pre></div>\n</section>\n</section>","rootId":"aggregation","toc":[{"title":"速查表","anchor":"cheat-sheet","children":[]},{"title":"在 QuerySet 上生成聚合","anchor":"generating-aggregates-over-a-queryset","children":[]},{"title":"为 QuerySet 中的每一个条目生成聚合","anchor":"generating-aggregates-for-each-item-in-a-queryset","children":[{"title":"组合多个聚合","anchor":"combining-multiple-aggregations","children":[]}]},{"title":"连接(Joins)和聚合","anchor":"joins-and-aggregates","children":[{"title":"反向关系","anchor":"following-relationships-backwards","children":[]}]},{"title":"聚合和其他 QuerySet 子句","anchor":"aggregations-and-other-queryset-clauses","children":[{"title":"filter() 和 exclude()","anchor":"filter-and-exclude","children":[{"title":"过滤注解","anchor":"filtering-on-annotations","children":[]},{"title":"annotate() 和 filter() 子句的顺序","anchor":"order-of-annotate-and-filter-clauses","children":[]}]},{"title":"order_by()","anchor":"order-by","children":[]},{"title":"values()","anchor":"values","children":[{"title":"annotate() 和 values() 的顺序","anchor":"order-of-annotate-and-values-clauses","children":[]},{"title":"与默认排序或 order_by() 交互","anchor":"interaction-with-default-ordering-or-order-by","children":[]}]},{"title":"聚合注解","anchor":"aggregating-annotations","children":[]}]}],"breadcrumbs":[{"docname":"topics/index","title":"使用 Django","url":"/zh-hans/2.2/topics/"},{"docname":"topics/db/index","title":"模型和数据库","url":"/zh-hans/2.2/topics/db/"}],"prev":{"docname":"topics/db/queries","title":"执行查询","url":"/zh-hans/2.2/topics/db/queries/"},"next":{"docname":"topics/db/search","title":"搜索","url":"/zh-hans/2.2/topics/db/search/"},"formats":{"html":"/zh-hans/2.2/topics/db/aggregation/","markdown":"/zh-hans/2.2/topics/db/aggregation.md","json":"/zh-hans/2.2/topics/db/aggregation.json"},"source":"https://github.com/django/django/blob/stable/2.2.x/docs/topics/db/aggregation.txt","official":"https://docs.djangoproject.com/zh-hans/2.2/topics/db/aggregation/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","zh-hans","fr","ja","id","pt-br","ko","es","el","pl"]}