mirror of https://github.com/django/django.git
Mention backward relationships in aggregate docs.
Thanks Anssi and Marc Tamlyn for reviewing. Fixes #19803.
This commit is contained in:
parent
5a3d9490f1
commit
0560bfb705
|
@ -21,14 +21,12 @@ used to track the inventory for a series of online bookstores:
|
||||||
class Author(models.Model):
|
class Author(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
age = models.IntegerField()
|
age = models.IntegerField()
|
||||||
friends = models.ManyToManyField('self', blank=True)
|
|
||||||
|
|
||||||
class Publisher(models.Model):
|
class Publisher(models.Model):
|
||||||
name = models.CharField(max_length=300)
|
name = models.CharField(max_length=300)
|
||||||
num_awards = models.IntegerField()
|
num_awards = models.IntegerField()
|
||||||
|
|
||||||
class Book(models.Model):
|
class Book(models.Model):
|
||||||
isbn = models.CharField(max_length=9)
|
|
||||||
name = models.CharField(max_length=300)
|
name = models.CharField(max_length=300)
|
||||||
pages = models.IntegerField()
|
pages = models.IntegerField()
|
||||||
price = models.DecimalField(max_digits=10, decimal_places=2)
|
price = models.DecimalField(max_digits=10, decimal_places=2)
|
||||||
|
@ -40,6 +38,7 @@ used to track the inventory for a series of online bookstores:
|
||||||
class Store(models.Model):
|
class Store(models.Model):
|
||||||
name = models.CharField(max_length=300)
|
name = models.CharField(max_length=300)
|
||||||
books = models.ManyToManyField(Book)
|
books = models.ManyToManyField(Book)
|
||||||
|
registered_users = models.PositiveIntegerField()
|
||||||
|
|
||||||
Cheat sheet
|
Cheat sheet
|
||||||
===========
|
===========
|
||||||
|
@ -64,6 +63,9 @@ In a hurry? Here's how to do common aggregate queries, assuming the models above
|
||||||
>>> Book.objects.all().aggregate(Max('price'))
|
>>> Book.objects.all().aggregate(Max('price'))
|
||||||
{'price__max': Decimal('81.20')}
|
{'price__max': Decimal('81.20')}
|
||||||
|
|
||||||
|
# All the following queries involve traversing the Book<->Publisher
|
||||||
|
# many-to-many relationship backward
|
||||||
|
|
||||||
# Each publisher, each with a count of books as a "num_books" attribute.
|
# Each publisher, each with a count of books as a "num_books" attribute.
|
||||||
>>> from django.db.models import Count
|
>>> from django.db.models import Count
|
||||||
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
|
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
|
||||||
|
@ -73,7 +75,6 @@ In a hurry? Here's how to do common aggregate queries, assuming the models above
|
||||||
73
|
73
|
||||||
|
|
||||||
# The top 5 publishers, in order by number of books.
|
# The top 5 publishers, in order by number of books.
|
||||||
>>> from django.db.models import Count
|
|
||||||
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
|
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
|
||||||
>>> pubs[0].num_books
|
>>> pubs[0].num_books
|
||||||
1323
|
1323
|
||||||
|
@ -169,7 +170,7 @@ specify the annotation::
|
||||||
Unlike ``aggregate()``, ``annotate()`` is *not* a terminal clause. The output
|
Unlike ``aggregate()``, ``annotate()`` is *not* a terminal clause. The output
|
||||||
of the ``annotate()`` clause is a ``QuerySet``; this ``QuerySet`` can be
|
of the ``annotate()`` clause is a ``QuerySet``; this ``QuerySet`` can be
|
||||||
modified using any other ``QuerySet`` operation, including ``filter()``,
|
modified using any other ``QuerySet`` operation, including ``filter()``,
|
||||||
``order_by``, or even additional calls to ``annotate()``.
|
``order_by()``, or even additional calls to ``annotate()``.
|
||||||
|
|
||||||
Joins and aggregates
|
Joins and aggregates
|
||||||
====================
|
====================
|
||||||
|
@ -205,6 +206,50 @@ issue the query::
|
||||||
|
|
||||||
>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
|
>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
|
||||||
|
|
||||||
|
Following relationships backwards
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
In a way similar to :ref:`lookups-that-span-relationships`, aggregations and
|
||||||
|
annotations on fields of models or models that are related to the one you are
|
||||||
|
querying can include traversing "reverse" relationships. The lowercase name
|
||||||
|
of related models and double-underscores are used here too.
|
||||||
|
|
||||||
|
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 Count, Min, Sum, Max, Avg
|
||||||
|
>>> Publisher.objects.annotate(Count('book'))
|
||||||
|
|
||||||
|
(Every Publisher in the resulting QuerySet will have an extra attribute called
|
||||||
|
``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'))
|
||||||
|
|
||||||
|
(The resulting dictionary will have a key called ``'oldest_pubdate'``. If no
|
||||||
|
such alias was specified, it would be the rather long ``'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 he/she 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'))
|
||||||
|
|
||||||
|
(Every Author in the resulting QuerySet will have an extra attribute called
|
||||||
|
``total_pages``. If no such alias was specified, it would be the rather long
|
||||||
|
``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'))
|
||||||
|
|
||||||
|
(The resulting dictionary will have a key called ``'average__rating'``. If no
|
||||||
|
such alias was specified, it would be the rather long ``'book__rating__avg'``.)
|
||||||
|
|
||||||
Aggregations and other QuerySet clauses
|
Aggregations and other QuerySet clauses
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue