{"title":"聚合","version":"6.1","locale":"zh-hans","docname":"topics/db/aggregation","url":"/zh-hans/6.1/topics/db/aggregation/","canonical":"https://djangodocs.dev/zh-hans/6.1/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\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\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\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\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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console code\"><code><span class=\"go\"># Total number of books.</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>count<span class=\"p\">()</span>\n<span class=\"go\">2452</span>\n\n<span class=\"go\"># Total number of books with publisher=BaloneyPress</span>\n<span class=\"gp\">&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=\"s2\">&quot;BaloneyPress&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>count<span class=\"p\">()</span>\n<span class=\"go\">73</span>\n\n<span class=\"go\"># Average price across all books, provide default to be returned instead</span>\n<span class=\"go\"># of None if no books exist.</span>\n<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>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</span><span class=\"p\">,</span> default<span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;price__avg&#39;: 34.35}</span>\n\n<span class=\"go\"># Max price across all books, provide default to be returned instead of</span>\n<span class=\"go\"># None if no books exist.</span>\n<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\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Max<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</span><span class=\"p\">,</span> default<span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;price__max&#39;: Decimal(&#39;81.20&#39;)}</span>\n\n<span class=\"go\"># Difference between the highest priced book and the average price of all books.</span>\n<span class=\"gp\">&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=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>aggregate<span class=\"p\">(</span>\n<span class=\"gp\">... </span>    price_diff<span class=\"o\">=</span>Max<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</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=\"s2\">&quot;price&quot;</span><span class=\"p\">)</span>\n<span class=\"gp\">... </span><span class=\"p\">)</span>\n<span class=\"go\">{&#39;price_diff&#39;: 46.85}</span>\n\n<span class=\"go\"># All the following queries involve traversing the Book&lt;-&gt;Publisher</span>\n<span class=\"go\"># foreign key relationship backwards.</span>\n\n<span class=\"go\"># Each publisher, each with a count of books as a &quot;num_books&quot; attribute.</span>\n<span class=\"gp\">&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=\"gp\">&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=\"s2\">&quot;book&quot;</span><span class=\"p\">))</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>pubs\n<span class=\"go\">&lt;QuerySet [&lt;Publisher: BaloneyPress&gt;, &lt;Publisher: SalamiPress&gt;, ...]&gt;</span>\n<span class=\"gp\">&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=\"go\">73</span>\n\n<span class=\"go\"># Each publisher, with a separate count of books with a rating above and below 5</span>\n<span class=\"gp\">&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=\"gp\">&gt;&gt;&gt; </span>above_5 <span class=\"o\">=</span> Count<span class=\"p\">(</span><span class=\"s2\">&quot;book&quot;</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=\"gp\">&gt;&gt;&gt; </span>below_5 <span class=\"o\">=</span> Count<span class=\"p\">(</span><span class=\"s2\">&quot;book&quot;</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=\"gp\">&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=\"gp\">&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=\"go\">23</span>\n<span class=\"gp\">&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=\"go\">12</span>\n\n<span class=\"go\"># The top 5 publishers, in order by number of books.</span>\n<span class=\"gp\">&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=\"s2\">&quot;book&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span>order_by<span class=\"p\">(</span><span class=\"s2\">&quot;-num_books&quot;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n<span class=\"gp\">&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=\"go\">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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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\">QuerySet</code> 上附加一个 <code class=\"docutils literal notranslate\">aggregate()</code> 子句来实现：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;price&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;price&quot;</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/6.1/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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;price&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;price&quot;</span><span class=\"p\">),</span> Max<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</span><span class=\"p\">),</span> Min<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</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/6.1/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/6.1/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/6.1/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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console code\"><code><span class=\"go\"># Build an annotated queryset</span>\n<span class=\"gp\">&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=\"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=\"s2\">&quot;authors&quot;</span><span class=\"p\">))</span>\n<span class=\"go\"># Interrogate the first object in the queryset</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n<span class=\"go\">&lt;Book: The Definitive Guide to Django&gt;</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=\"go\"># Interrogate the second object in the queryset</span>\n<span class=\"gp\">&gt;&gt;&gt; </span>q<span class=\"p\">[</span><span class=\"mi\">1</span><span class=\"p\">]</span>\n<span class=\"go\">&lt;Book: Practical Django Projects&gt;</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>authors__count\n<span class=\"go\">1</span>\n</code></pre></div>\n<p>与 <code class=\"docutils literal notranslate\">aggregate()</code> 一样，注释的名称是从聚合函数的名称和被聚合字段的名称自动派生的。您可以通过在指定注释时提供别名来覆盖这个默认名称：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;authors&quot;</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> <em>不</em> 是终端子句。<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> 操作进行修改，包括 <code class=\"docutils literal notranslate\">filter()</code>, <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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;authors&quot;</span><span class=\"p\">),</span> Count<span class=\"p\">(</span><span class=\"s2\">&quot;store&quot;</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/6.1/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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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>\n<span class=\"gp\">... </span>    Count<span class=\"p\">(</span><span class=\"s2\">&quot;authors&quot;</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=\"s2\">&quot;store&quot;</span><span class=\"p\">,</span> distinct<span class=\"o\">=</span><span class=\"kc\">True</span><span class=\"p\">)</span>\n<span class=\"gp\">... </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 允许你使用与过滤器引用相关字段时相同的 <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/db/queries/#field-lookups-intro\"><span class=\"std std-ref\">双下划线表示法</span></a>。然后，Django 将处理任何所需的表连接以检索和聚合相关值。</p>\n<p>例如，要找出每家商店提供的书籍价格范围，您可以使用以下注释：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;books__price&quot;</span><span class=\"p\">),</span> max_price<span class=\"o\">=</span>Max<span class=\"p\">(</span><span class=\"s2\">&quot;books__price&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;books__price&quot;</span><span class=\"p\">),</span> max_price<span class=\"o\">=</span>Max<span class=\"p\">(</span><span class=\"s2\">&quot;books__price&quot;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>连接链可以根据需要深入。例如，要提取任何可售书籍中最年轻作者的年龄，可以发出以下查询：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;books__authors__age&quot;</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/6.1/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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book__pubdate&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book__pages&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book__rating&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;authors&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;price&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;authors&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console code\"><code><span class=\"gp\">&gt;&gt;&gt; </span>highly_rated <span class=\"o\">=</span> Count<span class=\"p\">(</span><span class=\"s2\">&quot;book&quot;</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=\"s2\">&quot;book&quot;</span><span class=\"p\">),</span> highly_rated_books<span class=\"o\">=</span>highly_rated<span class=\"p\">)</span>\n</code></pre></div>\n<p>结果集中的每个 <code class=\"docutils literal notranslate\">Author</code> 都有 <code class=\"docutils literal notranslate\">num_books</code> 和 <code class=\"docutils literal notranslate\">highly_rated_books</code> 属性。参见 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/conditional-expressions/#conditional-aggregation\"><span class=\"std std-ref\">条件聚合</span></a>。</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book&quot;</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>\n<span class=\"gp\">... </span>    book__rating__gt<span class=\"o\">=</span><span class=\"mf\">3.0</span>\n<span class=\"gp\">... </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=\"s2\">&quot;book&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book__rating&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span>filter<span class=\"p\">(</span>\n<span class=\"gp\">... </span>    book__rating__gt<span class=\"o\">=</span><span class=\"mf\">3.0</span>\n<span class=\"gp\">... </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>\n<span class=\"gp\">... </span>    avg_rating<span class=\"o\">=</span>Avg<span class=\"p\">(</span><span class=\"s2\">&quot;book__rating&quot;</span><span class=\"p\">)</span>\n<span class=\"gp\">... </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 查询，因此当有疑问时，请使用 <code class=\"docutils literal notranslate\">str(queryset.query)</code> 检查 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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;authors&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span>order_by<span class=\"p\">(</span><span class=\"s2\">&quot;num_authors&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book__rating&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;name&quot;</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=\"s2\">&quot;book__rating&quot;</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;book__rating&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span>values<span class=\"p\">(</span>\n<span class=\"gp\">... </span>    <span class=\"s2\">&quot;name&quot;</span><span class=\"p\">,</span> <span class=\"s2\">&quot;average_rating&quot;</span>\n<span class=\"gp\">... </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-order-by\">\n<span id=\"aggregation-ordering-interaction\"></span><h4>Interaction with <code class=\"docutils literal notranslate\">order_by()</code><a class=\"heading-anchor\" href=\"#interaction-with-order-by\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\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\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</code></pre></div>\n<p>如果您想计算在有序查询集中每个不同的 <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>items <span class=\"o\">=</span> Item<span class=\"o\">.</span>objects<span class=\"o\">.</span>order_by<span class=\"p\">(</span><span class=\"s2\">&quot;name&quot;</span><span class=\"p\">)</span>\n<span class=\"c1\"># Warning: not quite correct!</span>\nitems<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>items<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/6.1/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>You might reasonably ask why Django doesn't remove the extraneous columns\nfor you. The main reason is consistency with <code class=\"docutils literal notranslate\">distinct()</code> and other\nplaces: Django <strong>never</strong> removes ordering constraints that you have\nspecified <em>explicitly with</em> <code class=\"docutils literal notranslate\">order_by()</code> (and we can't change those\nother methods' behavior, as that would violate our\n<a class=\"reference internal\" href=\"/zh-hans/6.1/misc/api-stability/\"><span class=\"doc\">API 的稳定性</span></a> policy).</p>\n</aside>\n<aside class=\"admonition-default-ordering-not-applied-to-group-by admonition\" role=\"note\">\n<p class=\"admonition-title\">Default ordering not applied to GROUP BY</p>\n<p><code class=\"docutils literal notranslate\">GROUP BY</code> queries (for example, those using <code class=\"docutils literal notranslate\">.values()</code> and\n<code class=\"docutils literal notranslate\">.annotate()</code>) don't use the model's default ordering.\nUse <code class=\"docutils literal notranslate\">order_by()</code> explicitly when a given order is needed.</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=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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=\"s2\">&quot;authors&quot;</span><span class=\"p\">))</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s2\">&quot;num_authors&quot;</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 id=\"aggregating-on-empty-querysets-or-groups\">\n<h3>在空查询集或组上进行聚合操作时，需要格外小心，因为这可能会导致未定义的行为或错误。在执行聚合操作之前，通常应确保查询集或组中包含足够的数据以执行所需的聚合计算。如果查询集或组为空，可以使用条件语句来避免聚合错误或不必要的操作。<a class=\"heading-anchor\" href=\"#aggregating-on-empty-querysets-or-groups\"><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/6.1/ref/models/querysets/#aggregate-default\"><span class=\"std std-ref\">default</span></a> 参数，通常是 <code class=\"docutils literal notranslate\">None</code>。这种行为发生是因为当执行的查询不返回任何行时，聚合函数会返回 <code class=\"docutils literal notranslate\">NULL</code>。</p>\n<p>您可以为大多数聚合操作提供 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#aggregate-default\"><span class=\"std std-ref\">default</span></a> 参数来指定返回值。但是，由于 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#django.db.models.Count\" title=\"django.db.models.Count\"><code class=\"xref py py-class docutils literal notranslate\">Count</code></a> 不支持 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#aggregate-default\"><span class=\"std std-ref\">default</span></a> 参数，它在空的查询集或分组上始终返回 <code class=\"docutils literal notranslate\">0</code>。</p>\n<p>例如，假设没有一本书的名称中包含 <em>web</em>，那么计算这本书集合的总价格会返回 <code class=\"docutils literal notranslate\">None</code>，因为没有匹配的行来进行 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#django.db.models.Sum\" title=\"django.db.models.Sum\"><code class=\"xref py py-class docutils literal notranslate\">Sum</code></a> 聚合计算：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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> Sum\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>filter<span class=\"p\">(</span>name__contains<span class=\"o\">=</span><span class=\"s2\">&quot;web&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Sum<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</span><span class=\"p\">))</span>\n<span class=\"go\">{&quot;price__sum&quot;: None}</span>\n</code></pre></div>\n<p>然而，可以在调用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#django.db.models.Sum\" title=\"django.db.models.Sum\"><code class=\"xref py py-class docutils literal notranslate\">Sum</code></a> 时设置 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#aggregate-default\"><span class=\"std std-ref\">default</span></a> 参数，以返回不同的默认值，如果找不到书籍：</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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__contains<span class=\"o\">=</span><span class=\"s2\">&quot;web&quot;</span><span class=\"p\">)</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Sum<span class=\"p\">(</span><span class=\"s2\">&quot;price&quot;</span><span class=\"p\">,</span> default<span class=\"o\">=</span><span class=\"mi\">0</span><span class=\"p\">))</span>\n<span class=\"go\">{&quot;price__sum&quot;: Decimal(&quot;0&quot;)}</span>\n</code></pre></div>\n<p>在内部，通过使用 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/database-functions/#django.db.models.functions.Coalesce\" title=\"django.db.models.functions.Coalesce\"><code class=\"xref py py-class docutils literal notranslate\">Coalesce</code></a> 包装聚合函数来实现 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#aggregate-default\"><span class=\"std std-ref\">default</span></a> 参数。这样可以在计算聚合时处理默认值。</p>\n</section>\n<section id=\"aggregating-with-mysql-only-full-group-by-enabled\">\n<span id=\"aggregation-mysql-only-full-group-by\"></span><h3>Aggregating with MySQL <code class=\"docutils literal notranslate\">ONLY_FULL_GROUP_BY</code> enabled<a class=\"heading-anchor\" href=\"#aggregating-with-mysql-only-full-group-by-enabled\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>When using the <code class=\"docutils literal notranslate\">values()</code> clause to group query results for annotations in\nMySQL with the <code class=\"docutils literal notranslate\">ONLY_FULL_GROUP_BY</code> SQL mode enabled, you may need to apply\n<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#django.db.models.AnyValue\" title=\"django.db.models.AnyValue\"><code class=\"xref py py-class docutils literal notranslate\">AnyValue</code></a> if the annotation includes a mix of\naggregate and non-aggregate expressions.</p>\n<p>Take the following example:</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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> F<span class=\"p\">,</span> Count<span class=\"p\">,</span> Greatest\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>values<span class=\"p\">(</span>greatest_pages<span class=\"o\">=</span>Greatest<span class=\"p\">(</span><span class=\"s2\">&quot;pages&quot;</span><span class=\"p\">,</span> <span class=\"mi\">600</span><span class=\"p\">))</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>\n<span class=\"gp\">... </span>    num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s2\">&quot;authors&quot;</span><span class=\"p\">),</span>\n<span class=\"gp\">... </span>    pages_per_author<span class=\"o\">=</span>F<span class=\"p\">(</span><span class=\"s2\">&quot;greatest_pages&quot;</span><span class=\"p\">)</span> <span class=\"o\">/</span> F<span class=\"p\">(</span><span class=\"s2\">&quot;num_authors&quot;</span><span class=\"p\">),</span>\n<span class=\"gp\">... </span><span class=\"p\">)</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s2\">&quot;pages_per_author&quot;</span><span class=\"p\">))</span>\n</code></pre></div>\n<p>This creates groups of books based on the SQL column <code class=\"docutils literal notranslate\">GREATEST(pages, 600)</code>.\nOne unique group consists of books with 600 pages or less, and other unique\ngroups will consist of books with the same pages. The <code class=\"docutils literal notranslate\">pages_per_author</code>\nannotation is composed of aggregate and non-aggregate expressions,\n<code class=\"docutils literal notranslate\">num_authors</code> is an aggregate expression while <code class=\"docutils literal notranslate\">greatest_page</code> isn't.</p>\n<p>Since the grouping is based on the <code class=\"docutils literal notranslate\">greatest_pages</code> expression, MySQL may be\nunable to determine that <code class=\"docutils literal notranslate\">greatest_pages</code> (used in the <code class=\"docutils literal notranslate\">pages_per_author</code>\nexpression) is functionally dependent on the grouped column. As a result, it\nmay raise an error like:</p>\n<div class=\"code-block\" data-language=\"pytb\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Pytb</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=\"Pytb code\"><code><span class=\"x\">OperationalError: (1055, &quot;Expression #2 of SELECT list is not in GROUP BY</span>\n<span class=\"x\">clause and contains nonaggregated column &#39;book_book.pages&#39; which is not</span>\n<span class=\"x\">functionally dependent on columns in GROUP BY clause; this is incompatible</span>\n<span class=\"x\">with sql_mode=only_full_group_by&quot;)</span>\n</code></pre></div>\n<p>To avoid this, you can wrap the non-aggregate expression with\n<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#django.db.models.AnyValue\" title=\"django.db.models.AnyValue\"><code class=\"xref py py-class docutils literal notranslate\">AnyValue</code></a>.</p>\n<div class=\"code-block\" data-language=\"pycon\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python console</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 console 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> AnyValue<span class=\"p\">,</span> F<span class=\"p\">,</span> Count<span class=\"p\">,</span> Greatest\n<span class=\"gp\">&gt;&gt;&gt; </span>Book<span class=\"o\">.</span>objects<span class=\"o\">.</span>values<span class=\"p\">(</span>\n<span class=\"gp\">... </span>    greatest_pages<span class=\"o\">=</span>Greatest<span class=\"p\">(</span><span class=\"s2\">&quot;pages&quot;</span><span class=\"p\">,</span> <span class=\"mi\">600</span><span class=\"p\">),</span>\n<span class=\"gp\">... </span><span class=\"p\">)</span><span class=\"o\">.</span>annotate<span class=\"p\">(</span>\n<span class=\"gp\">... </span>    num_authors<span class=\"o\">=</span>Count<span class=\"p\">(</span><span class=\"s2\">&quot;authors&quot;</span><span class=\"p\">),</span>\n<span class=\"gp\">... </span>    pages_per_author<span class=\"o\">=</span>AnyValue<span class=\"p\">(</span>F<span class=\"p\">(</span><span class=\"s2\">&quot;greatest_pages&quot;</span><span class=\"p\">))</span> <span class=\"o\">/</span> F<span class=\"p\">(</span><span class=\"s2\">&quot;num_authors&quot;</span><span class=\"p\">),</span>\n<span class=\"gp\">... </span><span class=\"p\">)</span><span class=\"o\">.</span>aggregate<span class=\"p\">(</span>Avg<span class=\"p\">(</span><span class=\"s2\">&quot;pages_per_author&quot;</span><span class=\"p\">))</span>\n<span class=\"go\">{&#39;pages_per_author__avg&#39;: 532.57143333}</span>\n</code></pre></div>\n<p>Other supported databases do not encounter the <code class=\"docutils literal notranslate\">OperationalError</code> in the\nexample above because they can detect the functional dependency. In general,\n<code class=\"docutils literal notranslate\">AnyValue</code> is useful when dealing with select list columns that involve\nnon-aggregate functions or complex expressions not recognized by the database\nas functionally dependent on the columns in the grouping clause.</p>\n<aside class=\"version-note version-changed\" data-version=\"6.0\" role=\"note\">\n<p class=\"version-note-title\">Changed in Django 6.0</p><p>The <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/models/querysets/#django.db.models.AnyValue\" title=\"django.db.models.AnyValue\"><code class=\"xref py py-class docutils literal notranslate\">AnyValue</code></a> aggregate was added.</p>\n</aside>\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":"Interaction with order_by()","anchor":"interaction-with-order-by","children":[]}]},{"title":"聚合注解","anchor":"aggregating-annotations","children":[]},{"title":"在空查询集或组上进行聚合操作时，需要格外小心，因为这可能会导致未定义的行为或错误。在执行聚合操作之前，通常应确保查询集或组中包含足够的数据以执行所需的聚合计算。如果查询集或组为空，可以使用条件语句来避免聚合错误或不必要的操作。","anchor":"aggregating-on-empty-querysets-or-groups","children":[]},{"title":"Aggregating with MySQL ONLY_FULL_GROUP_BY enabled","anchor":"aggregating-with-mysql-only-full-group-by-enabled","children":[]}]}],"breadcrumbs":[{"docname":"topics/index","title":"使用 Django","url":"/zh-hans/6.1/topics/"},{"docname":"topics/db/index","title":"模型和数据库","url":"/zh-hans/6.1/topics/db/"}],"prev":{"docname":"topics/db/fetch-modes","title":"Fetch modes","url":"/zh-hans/6.1/topics/db/fetch-modes/"},"next":{"docname":"topics/db/search","title":"搜索","url":"/zh-hans/6.1/topics/db/search/"},"formats":{"html":"/zh-hans/6.1/topics/db/aggregation/","markdown":"/zh-hans/6.1/topics/db/aggregation.md","json":"/zh-hans/6.1/topics/db/aggregation.json"},"source":"https://github.com/django/django/blob/stable/6.1.x/docs/topics/db/aggregation.txt","official":"https://docs.djangoproject.com/zh-hans/6.1/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","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}