---
title: "Pengumpulan"
version: 5.2
locale: id
source: https://docs.djangoproject.com/id/5.2/topics/db/aggregation/
canonical: https://djangodocs.dev/id/5.2/topics/db/aggregation/
---
# Pengumpulan

Panduan topik pada [Django's database-abstraction API](/id/5.2/topics/db/queries/) menggambarkan cara dimana anda dapat menggunakan permintaan Django yang membuat, mengambil, memperbaharui dan menghapus obyek tersendiri. Bagaimanapun, terkadang anda akan butuh mengambil nilai yang berasal oleh meringkas atau\*mengumpulkan\* kumpulan dari obyek. Topik panduan ini menggambarkan cara yang mengumpulkan nilai-nilai dapat dibangkitkan dan dikembalikan menggunakan permintaan Django.

Sepanjang panduan ini, kami akan mengacu ke model berikut. Model-model ini digunakan untuk melacak inventaris untuk rangkaian dari toko buku daring:

```python
from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

class Publisher(models.Model):
    name = models.CharField(max_length=300)

class Book(models.Model):
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    rating = models.FloatField()
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    pubdate = models.DateField()

class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)
```

## Lembar curang

Terburu-buru? Ini adalah bagaimana melakukan permintaan pengumpulan umum, menganggap model diatas:

```pycon
# Total number of books.
>>> Book.objects.count()
2452

# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name="BaloneyPress").count()
73

# Average price across all books, provide default to be returned instead
# of None if no books exist.
>>> from django.db.models import Avg
>>> Book.objects.aggregate(Avg("price", default=0))
{'price__avg': 34.35}

# Max price across all books, provide default to be returned instead of
# None if no books exist.
>>> from django.db.models import Max
>>> Book.objects.aggregate(Max("price", default=0))
{'price__max': Decimal('81.20')}

# Difference between the highest priced book and the average price of all books.
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
...     price_diff=Max("price", output_field=FloatField()) - Avg("price")
... )
{'price_diff': 46.85}

# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.

# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count("book"))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73

# Each publisher, with a separate count of books with a rating above and below 5
>>> from django.db.models import Q
>>> above_5 = Count("book", filter=Q(book__rating__gt=5))
>>> below_5 = Count("book", filter=Q(book__rating__lte=5))
>>> pubs = Publisher.objects.annotate(below_5=below_5).annotate(above_5=above_5)
>>> pubs[0].above_5
23
>>> pubs[0].below_5
12

# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count("book")).order_by("-num_books")[:5]
>>> pubs[0].num_books
1323
```

## Membangkitkan pengumpulan terhadap `QuerySet`

Django provides two ways to generate aggregates. The first way is to generate
summary values over an entire `QuerySet`. For example, say you wanted to
calculate the average price of all books available for sale. Django's query
syntax provides a means for describing the set of all books:

```pycon
>>> Book.objects.all()
```

What we need is a way to calculate summary values over the objects that
belong to this `QuerySet`. This is done by appending an `aggregate()`
clause onto the `QuerySet`:

```pycon
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg("price"))
{'price__avg': 34.35}
```

The `all()` is redundant in this example, so this could be simplified to:

```pycon
>>> Book.objects.aggregate(Avg("price"))
{'price__avg': 34.35}
```

Argumen pada klausa `aggregate()` menggambarkan nilai pengumpulan yang kami ingin hitung - dalam kasus ini, rata-rata dari bidang `price` pada model `Book`. Sebuah daftar dari fungsi pengumpulan yang tersedia dapat ditemukan di [QuerySet reference](/id/5.2/ref/models/querysets/#aggregation-functions).

`aggregate()` is a terminal clause for a `QuerySet` that, when invoked,
returns a dictionary of name-value pairs. The name is an identifier for the
aggregate value; the value is the computed aggregate. The name is
automatically generated from the name of the field and the aggregate function.
If you want to manually specify a name for the aggregate value, you can do so
by providing that name when you specify the aggregate clause:

```pycon
>>> Book.objects.aggregate(average_price=Avg("price"))
{'average_price': 34.35}
```

If you want to generate more than one aggregate, you add another argument to
the `aggregate()` clause. So, if we also wanted to know the maximum and
minimum price of all books, we would issue the query:

```pycon
>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
```

## Membangkitkan pengumpulan untuk setiap barang di `QuerySet`

The second way to generate summary values is to generate an independent
summary for each object in a [`QuerySet`](/id/5.2/ref/models/querysets/#django.db.models.query.QuerySet). For example, if you are
retrieving a list of books, you may want to know how many authors contributed
to each book. Each Book has a many-to-many relationship with the Author; we
want to summarize this relationship for each book in the `QuerySet`.

Per-object summaries can be generated using the
[`annotate()`](/id/5.2/ref/models/querysets/#django.db.models.query.QuerySet.annotate) clause. When an `annotate()` clause is
specified, each object in the `QuerySet` will be annotated with the
specified values.

The syntax for these annotations is identical to that used for the
[`aggregate()`](/id/5.2/ref/models/querysets/#django.db.models.query.QuerySet.aggregate) clause. Each argument to `annotate()` describes
an aggregate that is to be calculated. For example, to annotate books with the
number of authors:

```pycon
# Build an annotated queryset
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count("authors"))
# Interrogate the first object in the queryset
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1
```

As with `aggregate()`, the name for the annotation is automatically derived
from the name of the aggregate function and the name of the field being
aggregated. You can override this default name by providing an alias when you
specify the annotation:

```pycon
>>> q = Book.objects.annotate(num_authors=Count("authors"))
>>> q[0].num_authors
2
>>> q[1].num_authors
1
```

Tidak seperti `aggregate()`, `annotate()` adalah *bukan* klausa terminal. Keluaran dari klausa `annotate()` adalah sebuah `QuerySet`; `QuerySet` ini dapat dirubah menggunakan apapun selain operasi `QuerySet`, termasuk `filter()`, `order_by()`, atau bahkan tambahan panggilan pada `annotate()`.

### Memadukan banyak pengumpulan

Combining multiple aggregations with `annotate()` will [yield the
wrong results](https://code.djangoproject.com/ticket/10060) because joins are used instead of subqueries:

```pycon
>>> book = Book.objects.first()
>>> book.authors.count()
2
>>> book.store_set.count()
3
>>> q = Book.objects.annotate(Count("authors"), Count("store"))
>>> q[0].authors__count
6
>>> q[0].store__count
6
```

Untuk kebanyakan pengumpulan, tidak ada jalan menghindari masalah ini, bagaimanapun, pengumpulan [`Count`](/id/5.2/ref/models/querysets/#django.db.models.Count) mempunai sebuah parameter `distinct` yang mungkin membantu:

```pycon
>>> q = Book.objects.annotate(
...     Count("authors", distinct=True), Count("store", distinct=True)
... )
>>> q[0].authors__count
2
>>> q[0].store__count
3
```

> **Jika ragu, periksa permintaan SQL!**
>
> Untuk memahami apa yang terjadi di permintaan anda, pertimbangkan memeriksa sifat `query` dari `QuerySet` anda.

## Gabung dan kumpulkan

Sejauh ini, kami telah berurusan dengan pengumpulan terhadap bidang yang milik ke model sedang diminta. Bagaimanapun, terkadang nilai anda ingin kumpulkan akan milik ke model yang terkait ke model anda sedang meminta.

Ketika anda menentukan bidang untuk dikumpulkan dalam sebuah fungsi pengumpulan, Django akan mengizinkan anda menggunakan [double underscore notation](/id/5.2/topics/db/queries/#field-lookups-intro) sama yang digunakan ketika mengacu ke bidang terkait dalam penyaring. Django akan kemudian menangani gabungan table apapun yang dibutuhkan untuk mengambil dan mengumpulkan nilai terkait.

For example, to find the price range of books offered in each store,
you could use the annotation:

```pycon
>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min("books__price"), max_price=Max("books__price"))
```

Ini memberitahu Django untuk mengambil model `Store`, gabung (melalui hubungan many-to-many) dengan model `Book`, dan dikumpulkan pada bidang harga dari model buku untuk menghasilkan sebuah nilai minimal dan maksimal.

The same rules apply to the `aggregate()` clause. If you wanted to
know the lowest and highest price of any book that is available for sale
in any of the stores, you could use the aggregate:

```pycon
>>> Store.objects.aggregate(min_price=Min("books__price"), max_price=Max("books__price"))
```

Join chains can be as deep as you require. For example, to extract the
age of the youngest author of any book available for sale, you could
issue the query:

```pycon
>>> Store.objects.aggregate(youngest_age=Min("books__authors__age"))
```

### Mengikuti hubungan kebelakang

Dalam sebuah cara mirip pada [Pencarian yang menjangkau hubungan](/id/5.2/topics/db/queries/#lookups-that-span-relationships), pengumpulan dan catatan pada bidang dari model atau model yang terkait ke satu anda sedang meminta dapat menyertakan lintasan hubungan "reverse". Nama huruf kecil dari model terkait dan garis bawah ganda digunakan disini juga.

For example, we can ask for all publishers, annotated with their respective
total book stock counters (note how we use `'book'` to specify the
`Publisher` -\> `Book` reverse foreign key hop):

```pycon
>>> from django.db.models import Avg, Count, Min, Sum
>>> Publisher.objects.annotate(Count("book"))
```

(Setiap `Publisher` dalam menghasilkan `QuerySet` akan mempunyai sebuah atribut tambahan disebut `book__count`.)

We can also ask for the oldest book of any of those managed by every publisher:

```pycon
>>> Publisher.objects.aggregate(oldest_pubdate=Min("book__pubdate"))
```

(Hasil kamus akan mempunyai sebuah kunci dipanggil `'oldest_pubdate'`. Jika tidak ada nama lain seperti itu telah ditentukan, itu akan agak panjang `'book__pubdate__min'`.)

This doesn't apply just to foreign keys. It also works with many-to-many
relations. For example, we can ask for every author, annotated with the total
number of pages considering all the books the author has (co-)authored (note how we
use `'book'` to specify the `Author` -\> `Book` reverse many-to-many hop):

```pycon
>>> Author.objects.annotate(total_pages=Sum("book__pages"))
```

(Setiap `Author` dalam menghasilkan `QuerySet` akan mempunyai sebuah atribut tambahan dipanggil `total_pages`. Jika tidak ada nama lain itu, itu akan menjadi agak panjang `book__pages__sum`.)

Or ask for the average rating of all the books written by author(s) we have on
file:

```pycon
>>> Author.objects.aggregate(average_rating=Avg("book__rating"))
```

(Hasil kamus akan mempunyai sebuah kunci dipanggil `'average_rating'`. Jika tidak ada nama lain seperti itu telah ditentukan, itu akan agak panjang `'book__rating__avg'`.)

## Pengumpulan dan klausa `QuerySet` lain

### `filter()` dan `exclude()`

Pengumpulan dapat juga ikut serta dalam penyaring. `filter()` (atau\`\`exclude()\`\`) apapun berlaku pada bidang model biasa akan mempunyai pengaruh dari membatasi obyek yang dianggap untuk pengumpulan.

When used with an `annotate()` clause, a filter has the effect of
constraining the objects for which an annotation is calculated. For example,
you can generate an annotated list of all books that have a title starting
with "Django" using the query:

```pycon
>>> from django.db.models import Avg, Count
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count("authors"))
```

When used with an `aggregate()` clause, a filter has the effect of
constraining the objects over which the aggregate is calculated.
For example, you can generate the average price of all books with a
title that starts with "Django" using the query:

```pycon
>>> Book.objects.filter(name__startswith="Django").aggregate(Avg("price"))
```

#### Penyaringan pada keterangan

Nilai dicatat dapat juga disaring. Nama lain untuk catatan dapat digunaan dalam klausa `filter()` dan `exclude()` di cara sama seperti bidang model lainnya apapun.

For example, to generate a list of books that have more than one author,
you can issue the query:

```pycon
>>> Book.objects.annotate(num_authors=Count("authors")).filter(num_authors__gt=1)
```

Permintaan ini membangkitkan sebuah kumpulan hasil keterangan, dan kemudian membangkitkan sebuah penyaring berdasarkan pada keterangan itu.

If you need two annotations with two separate filters you can use the
`filter` argument with any aggregate. For example, to generate a list of
authors with a count of highly rated books:

```pycon
>>> highly_rated = Count("book", filter=Q(book__rating__gte=7))
>>> Author.objects.annotate(num_books=Count("book"), highly_rated_books=highly_rated)
```

Each `Author` in the result set will have the `num_books` and
`highly_rated_books` attributes. See also [Pengumpulan bersyarat](/id/5.2/ref/models/conditional-expressions/#conditional-aggregation).

> **Memilih diantara filter dan QuerySet.filter()**
>
> Hidnari menggunakan argumen `filter` dengan keterangan atau pengumpulan tunggal. Itu lebih efesien menggunakan `QuerySet.filter()` untuk tidak menyertakan baris. Argumen pengumpulan `filter` hanya berguna ketika menggunakan dua atau lebih pengumpulan terhadap hubungan sama dengan keadaan berbeda.

#### Urutan dari klausa `annotate()` dan `filter()`

Ketika mengembangkan sebuah permintaan rumit yang melibatkan kedua klausa `annotate()` dan `filter()`, berikan perhatian khusus pada urutan dimana klausa diberlakukan pada `QuerySet`.

Ketika sebuah klausa `annotate()` diberlakukan pada sebuah permintaan, catatan dihitung terhadap keadaan dari permintaan sampai titik dimana catatan diminta. Impliaksi praktik dari ini adalah bahwa `filter()` dan `annotate()` bukan operasi komutatif.

Diberikan:

- Penerbit A mempunyai dua buku dengan nilai 4 dan 5.
- Penerbit B mempunyai dua buku dengan nilai 1 dan 4.
- Penerbit C mempunyai satu buku dengan penilaian 1.

Berikut adalah contoh dengan kumpulan `Count`:

```pycon
>>> a, b = Publisher.objects.annotate(num_books=Count("book", distinct=True)).filter(
...     book__rating__gt=3.0
... )
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 2)

>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(num_books=Count("book"))
>>> a, a.num_books
(<Publisher: A>, 2)
>>> b, b.num_books
(<Publisher: B>, 1)
```

Kedua permintaan mengembalikan sebuah daftar dari penerbit yang mempunyai setidaknya satu buku dengan penilaian melebihi 3.0, karenanya penerbit C tidak disertakan.

Dalam permintaan pertama, catatan mendahului penyaring, jadi penyaring tidak mempunyai pengaruh pada catatan. `distinct=True` dibutuhkan untuk menghindari [query bug](#combining-multiple-aggregations).

Permintaan kedua menghitung jumlah buku yang mempunyai nilai melebihi 3.0 untuk setiap penerbit. Penyaring ini mendahului catatan, jadi batasan penyaring obyek dianggap ketika menghitung catatan.

Berikut contoh lain dengan kumpulan `Rata-Rata`:

```pycon
>>> a, b = Publisher.objects.annotate(avg_rating=Avg("book__rating")).filter(
...     book__rating__gt=3.0
... )
>>> a, a.avg_rating
(<Publisher: A>, 4.5)  # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 2.5)  # (1+4)/2

>>> a, b = Publisher.objects.filter(book__rating__gt=3.0).annotate(
...     avg_rating=Avg("book__rating")
... )
>>> a, a.avg_rating
(<Publisher: A>, 4.5)  # (5+4)/2
>>> b, b.avg_rating
(<Publisher: B>, 4.0)  # 4/1 (book with rating 1 excluded)
```

Permintaan pertama meminta untuk rata-rata penilaian dari semua penerbit buku untuk penerbit yang mempunyai setidaknya satu buku ketika penilaian melebihi 3.0. Permintaan kedua meminta untuk rata-rata dari penilaian buku penerbit untuk hanya mereka penilaian melebihi 3.0.

Itu adalah sangat sulit memahami bahwa ORM akan menterjemahkan queryset rumit kedalam permintaan SQL ketika dalam keraguan, periksa SQL dengan `str(queryset.query)` dan tulis jumlah besar percobaan.

### `order_by()`

Catatan dapat digunakan sebagai dasar untuk pengurutan. Ketika anda menentukan sebuah klausa `order_by()`, pengumpulan anda sediakan dapat mengacu nama lain apapun ditentukan sebagai bagian dari sebuah klausa `annotate()` dalam permintaan.

Misalnya, untuk mengurutkan `QuerySet` buku berdasarkan jumlah penulis yang berkontribusi pada buku, anda dapat menggunakan permintaan berikut:

```pycon
>>> Book.objects.annotate(num_authors=Count("authors")).order_by("num_authors")
```

### `values()`

Biasanya, catatan dibangkitkan pada dasar per-obyek - sebuah `QuerySet` dicatat akan mengembalikan satu hasil untuk setiap obyek dalam `QuerySet` asli. Bagaimanapun, ketika sebuah klausa `values()` digunakan untuk membatasi kolom yang dikembalikan dalam kumpulan hasil, metode untuk menilai catatan adak sedikit berbeda. Sebagai gantinya mengembalikan sebuah hasil dicatat untuk setiap hasil dalam `QuerySet` asli, hasil asli dikelompokkan menurut pada perpaduan unik dari bidang-bidang ditentukan dalam klausa `values()`.  Sebuah catatam kemudian disediakan untuk setiap kelompok unik; catatan dihitung terhadap semua anggota dari kelompok.

Untuk setiap contoh, pertimbangkan sebuah permintaan penulis yang berusaha menemukan rata-rata penilaian dari buku ditulis oleh setiap penulis.

```pycon
>>> Author.objects.annotate(average_rating=Avg("book__rating"))
```

Ini akan mengembalikan satu hasil untuk setiap penulis di basisdata, diberikan keterangan dengan nilai buku rata-rata mereka.

Namun, hasilnya akan sedikit berbeda jika anda menggunakan klausa `values()`:

```pycon
>>> Author.objects.values("name").annotate(average_rating=Avg("book__rating"))
```

Dalam contoh ini, penulis akan dikelompokkan berdasarkan nama, jadi anda akan hanya mendapatkan sebuah hasil dicatata untuk setiap nama penulis *unik*. Ini berarti jika anda mempunyai dua penulis dengan nama sama, hasil mereka akan digabung kedalam hasil tunggal di keluaran dari permintaan; rata-rata akan dihitung sebagai rata-rata terhadap buku ditulis oleh kedua penulis.

#### Urutan dari klausa `annotate()` dan `values()`

Seperti klausa `filter()`, urutan dimana klausa `annotate()` dan `values()` diberlakukan pada sebuah permintaan adalah signifikan. Jika klausa `values()` mendahului `annotated()`, catatan akan dihitung menggunakan pengelomppokan digambarkan oleh klausa `values()`.

Bagaimanapun, jika klausa `annotate()` mendahului klausa `values()`, catatan akan dibangkitkan terhadap keseluruhan kumpulan permintaan. Dalam kasus ini, klausa `values()` hanya membatasi bidang yang dibangkitkan pada keluaran.

Misalnya, jika kita membalikkan urutan klausa `values()` dan `annotate()` dari contoh kita sebelumnya:

```pycon
>>> Author.objects.annotate(average_rating=Avg("book__rating")).values(
...     "name", "average_rating"
... )
```

Ini akan sekarang menghasilkan hasil unik untuk setiap penulis; bagaimanapun, hanya nama penulis dan catatan `average_rating` akan dikembalikan dalam data keluaran.

Anda harus juga catat bahwa `average_rating` telah jelas disertakan dalam daftar dari nilai-nilai untuk dikembalikan. Ini dibutuhkan karena dari urutan dari klausa `values()` dan `annotate()`.

Jika klausa `values()` mendahului klausa `annotate()`, setiap catatan akan otomatis ditambahkan ke kumpulan hasil. Bagaimanapun, jika klausa `values()` diberlakukan setelah klausa `annotate()`, anda butuh jelas menyertakan kolom pengumpulan.

#### Interaksi dengan `order_by()`

Bidang yang disebutkan di bagian `order_by()` dari kumpulan permintaan digunakan saat memilih data keluaran, bahkan jika bidang tersebut tidak ditentukan lain dalam panggilan `values()`. Bidang tambahan ini digunakan untuk mengelompokkan hasil "suka" bersama-sama dan mereka dapat membuat baris hasil identik tampak terpisah. Ini muncul, terutama, saat menghitung sesuatu.

Berdasarkan cara contoh, seharusnya anda mempunyai model seperti ini:

```
from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=10)
    data = models.IntegerField()
```

If you want to count how many times each distinct `data` value appears in an
ordered queryset, you might try this:

```
items = Item.objects.order_by("name")
# Warning: not quite correct!
items.values("data").annotate(Count("id"))
```

...which will group the `Item` objects by their common `data` values and
then count the number of `id` values in each group. Except that it won't
quite work. The ordering by `name` will also play a part in the grouping, so
this query will group by distinct `(data, name)` pairs, which isn't what you
want. Instead, you should construct this queryset:

```
items.values("data").annotate(Count("id")).order_by()
```

...membersihkan pengurutan apapun di permintaan. Anda dapat juga mengurutkan berdasarkan, katakan, `data` tanpa pengaruh berbahaya apapun, sejak itu sudah bermain sebuah peran dalam permintaan.

Perilaku ini adalah sama seperti dicatat dalam dokumentasi queryset untuk [`distinct()`](/id/5.2/ref/models/querysets/#django.db.models.query.QuerySet.distinct) dan aturan umum adalah sama: biasanya anda tidak ingin kolom tambahan bermain bagian dalam hasil, jadi bersihkan pengurutan, atau setidaknya pastikan itu terbatas hanya bidang-bidang tersebut anda juga pilih dalam sebuah panggilan `values()`.

> **Note**
>
> You might reasonably ask why Django doesn't remove the extraneous columns
> for you. The main reason is consistency with `distinct()` and other
> places: Django **never** removes ordering constraints that you have
> specified *explicitly with* `order_by()` (and we can't change those
> other methods' behavior, as that would violate our
> [Keseimbangan API](/id/5.2/misc/api-stability/) policy).

> **Default ordering not applied to GROUP BY**
>
> `GROUP BY` queries (for example, those using `.values()` and
> `.annotate()`) don't use the model's default ordering.
> Use `order_by()` explicitly when a given order is needed.

### Pengumpulan catatan

Anda dapat juga membangkitkan sebuah pengumpulan pada hasil dari sebuah catatan. Ketika anda menentukan sebuah klausa `aggregate()`, pengumpulan anda sediakan dapat mengacu setiap nama lain ditentukan sebagai bagian dari sebuah klausa `annotated()` dalam permintaan.

For example, if you wanted to calculate the average number of authors per
book you first annotate the set of books with the author count, then
aggregate that author count, referencing the annotation field:

```pycon
>>> from django.db.models import Avg, Count
>>> Book.objects.annotate(num_authors=Count("authors")).aggregate(Avg("num_authors"))
{'num_authors__avg': 1.66}
```

### Aggregating on empty querysets or groups

When an aggregation is applied to an empty queryset or grouping, the result
defaults to its [default](/id/5.2/ref/models/querysets/#aggregate-default) parameter, typically
`None`. This behavior occurs because aggregate functions return `NULL` when
the executed query returns no rows.

You can specify a return value by providing the [default](/id/5.2/ref/models/querysets/#aggregate-default) argument for most aggregations. However, since
[`Count`](/id/5.2/ref/models/querysets/#django.db.models.Count) does not support the [default](/id/5.2/ref/models/querysets/#aggregate-default) argument, it will always return `0` for empty querysets
or groups.

For example, assuming that no book contains *web* in its name, calculating the
total price for this book set would return `None` since there are no matching
rows to compute the [`Sum`](/id/5.2/ref/models/querysets/#django.db.models.Sum) aggregation on:

```pycon
>>> from django.db.models import Sum
>>> Book.objects.filter(name__contains="web").aggregate(Sum("price"))
{"price__sum": None}
```

However, the [default](/id/5.2/ref/models/querysets/#aggregate-default) argument can be set when
calling [`Sum`](/id/5.2/ref/models/querysets/#django.db.models.Sum) to return a different default value if
no books can be found:

```pycon
>>> Book.objects.filter(name__contains="web").aggregate(Sum("price", default=0))
{"price__sum": Decimal("0")}
```

Under the hood, the [default](/id/5.2/ref/models/querysets/#aggregate-default) argument is implemented
by wrapping the aggregate function with
[`Coalesce`](/id/5.2/ref/models/database-functions/#django.db.models.functions.Coalesce).
