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¶
In a hurry? Here's how to do common aggregate queries, assuming the models above:
# 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.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'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 menyediakan dua cara membangkitkan pengumpulan. Cara pertama adalah membangkitkan nilai ringkasan terhadap keseluruhan QuerySet
. Sebagai contoh, katakan anda ingin menghitung rata-rata harga dari semua buku tersedia untuk dijual. Sintaksis permintaan Django menyediakan sarana untuk menggambarkan kumpulan dari semua buku:
>>> Book.objects.all()
Apa kami butuhkan adalah cara menghitung ringkasan nilai terhadap obyek yang memiliki QuerySet
ini. Ini diselesaikan dengan menambah sebuah klausa aggregate()
kedalam QuerySet
:
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
all()
berulang di contoh ini, jadi ini dapat disederhanakan menjadi:
>>> 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()
adalah klausa terminal untuk sebuah QuerySet
yang, ketika dipanggil, mengembalikan sebuah kamus dari pasangan nama-nilai. Nama adalah sebuah penciri untuk nilai pengumpulan; nilai adalah pengumpulan yang dihitung. Nama adalah otomatis dibangkitkan dari nama dari bidang dan fungsi pengumpulan. Jika anda ingin manual menentukan nama untuk nilai pengumpulan, anda dapat melakukannya dengan menyediakan nama itu ketika anda menentukan klausa pengumpulan:
>>> 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
Seperti aggregate()
, nama untuk catatan otomatis berasal dari nama dari fungsi pengumpulan dan nama dari bidang sedang dikumpulkan. Anda dapat menimpa nama awalan ini dengan menyediakan sebuah nama lain ketika anda menentukan catatan:
>>> 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.
Sebagai contoh, untuk menemukan jangkauan harga dari buku ditawarkan disetiap toko, anda dapat menggunakan catatan:
>>> 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.
Aturan sama berlaku pada klausa aggregate()
. Jika anda ingin mengetahui harga terendah dan tertinggi dari buku apapun yang tersedia untuk dijual di toko apapun, anda dapat menggunakan pengumpulan:
>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))
Ikatan join dapat sedalam seperti anda minta. Sebagai contoh, untuk mengeluarkan umur dari penulis termuda dari buku apapun yang tersedia untuk dijual, anda dapat menerbitkan permintaan:
>>> 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.
Sebagai contoh, kami dapat meminta untuk semua penerbit, dicatat dengan masing-masing jumlah penghitung stok buku (catat bahwa kami menggunakan 'book'
untuk menentukan Publisher
-> Book
membalikkan lompatan foreign key):
>>> 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
.)
Kami dapat juga meminta untuk buku tertua dari setiap dari itu dikelola oleh setiap penerbit:
>>> 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'
.)
Ini tidak berlaku hanya pada foreign key. Itu juga bekerja dengan hubungan many-to-many. Sebagai contoh, kami dapat meminta untuk setiap penulis, dicatat dengan jumlah nomor dari halaman mempertimbangkan semua buku penulis mempunyai penulis(-bersama) (catat bagaimana menggunakan 'book'
untuk menentukan Author
-> Book
membalikkan lompatan many-to-many):
>>> 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
.)
Atau minta untuk penilaian rata-rata dari semua buku ditulis oleh penulis kami punyai pada berkas:
>>> 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.
Ketika digunakan dengan sebuah klausa annotate()
, sebuah penyaring mempunyai pengaruh dari membatasi obyek untuk dimana sebuah catatan dihitung. Sebagai contoh, anda dapat membangkitkan daftar dari semua buku yang mempunyai sebuah judul dimulai dengan "Django"" menggunakan permintaan:
>>> from django.db.models import Avg, Count
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))
Ketika digunakan dengan sebuah klausa aggregate()
, sebuah penyaring mempunyai pengaruh dari membatasi obyek untuk dimana sebuah pengumpulan dihitung. Sebagai contoh, anda dapat membangkitkan rata-rata harga dari semua buku yang mempunyai sebuah judul dimulai dengan "Django"" menggunakan permintaan:
>>> 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.
Sebagai contoh, untuk membangkitkan sebuah daftar buku yang mempunyai lebih dari satu penulis, anda dapat menerbitkan permintaan:
>>> 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.
Jika anda butuh dua keterangan dengan dua penyaring terpisah anda dapat menggunakan argumen filter
dengan pengumpulan apapun. Sebagai contoh, untuk membangkitkan daftar dari penulis dengan jumlah peringkat buku tertinggi:
>>> highly_rated = Count('book', filter=Q(book__rating__gte=7))
>>> Author.objects.annotate(num_books=Count('book'), highly_rated_books=highly_rated)
Setiap Author
dalam kumpulan hasil akan memiliki atribut-atribut num_books
dan highly_rated_books
.
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.
Ini adalah sebuah contoh dengan pengumpulan 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.
Ini adalah sebuah contoh lain dengan pengumpulan Avg
:
>>> 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.
Sebagai contoh, untuk mengurutkan QuerySet
dari buku dengan jumlah dari pengarang yang memiliki bantuan 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.
Bagaimanapun, hasil akan sedikit berbeda jika anda menggunakan kalusa 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.
Sebagai contoh, jika kami membalikkan urutan dari klausa values()
dan annotate()
dari contoh kami 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 pengurutan awalan atau order_by()
¶
Bidang-bidang yang disebut di bagian order_by()
dari sebuah queryset (atau yang digunakan di pengurutan awalan pada sebuah model) digunakan ketika memilih data keluaran, bahkan jika mereka tidak sebaliknya ditentukan di panggilan values()
. Bidang tambahan ini digunakan untuk mengelompokkan hasil "like" bersama-sama dan mereka dapat membuat sebaliknya baris hasil mirip muncul untuk dipesahkan. Ini menunjukkan, khususnya, ketika menghitung hal-hal.
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()
class Meta:
ordering = ["name"]
Bagian terpenfting disini adalah pengurutan awalan pada bidang name
. Jika anda ingin menghitung seberapa banyak kali setiap perbedaan nilai data
muncul, anda mungkin mencoba ini:
# Warning: not quite correct!
Item.objects.values("data").annotate(Count("id"))
...yang akan mengelompokkan obyek Item
berdasarkan nilai-nilai data
umum mereka dan menghitung nomro dari nilai-nilai id
dalam setiap kelompok, Kecuali itu tidak akan bekerja. Pengurutan awalan oleh name
akan juga bermain bagian dalam pengelompokan, jadi permintaan ini akan dikelompokkan berdasarkan perbedaan pasangan (data, name)
, yang tidak apa anda inginkan. Malahan, anda harus membangun queryset ini:
Item.objects.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.
Sebagai contoh, jika anda ingin menjumlahkan angka rata-rata dari penulis per buku anda pertama membubuhi catatan kumpulan buku dengan jumlah penulis, lalu kumpulkan jumlah penulis itu, mengacu bidang membubuhi catatan
>>> from django.db.models import Avg, Count
>>> Book.objects.annotate(num_authors=Count('authors')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66}