Mention backward relationships in aggregate docs.

Thanks Anssi and Marc Tamlyn for reviewing.

Fixes #19803.
This commit is contained in:
Ramiro Morales 2013-02-11 12:49:30 -03:00
parent 5a3d9490f1
commit 0560bfb705
1 changed files with 49 additions and 4 deletions

View File

@ -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
======================================= =======================================