# Dokumentasi Django 4.2.9.dev20231204082947

# Pengumpulan

Panduan topik pada Django's database-abstraction API 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:

    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:

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

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

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

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

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

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

    >>> 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`. 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()` 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()` clause. Each argument to `annotate()` describes an aggregate that is to be calculated. For example, to annotate books with the number of authors:

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

    >>> 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 because joins are used instead of subqueries:

    >>> 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` mempunai sebuah parameter `distinct` yang mungkin membantu:

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

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

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

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

### Mengikuti hubungan kebelakang

Dalam sebuah cara mirip pada Pencarian yang menjangkau hubungan, 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):

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

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

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

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

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

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

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

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

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

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

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

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

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

    >>> 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()`:

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

    >>> 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()` 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()`.

Catatan

Anda mungkin layak bertanya mengapa Django tidak memidnahkan kolom tidak ada hubungannya untuk anda. Alasan utama adalah ketetapan dengan distinct()`` dan tempat lain: Django tidak pernah memindahkan pengurutan yang anda telah tentukan (dan kami tidak dapat merubah perilaku metode lain tersebut, ketika itu akan melanggar kebijakan Keseimbangan API kami).

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

    >>> 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 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 argument for most aggregations. However, since `Count` does not support the 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` aggregation on:

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

However, the default argument can be set when calling `Sum` to return a different default value if no books can be found:

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

Under the hood, the default argument is implemented by wrapping the aggregate function with `Coalesce`.