Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Mention backward relationships in aggregate docs.

Thanks Anssi and Marc Tamlyn for reviewing.

Fixes #19803.
  • Loading branch information...
commit 0560bfb705687c831e2769b1202706e2ceb1f7a7 1 parent 5a3d949
@ramiro ramiro authored
Showing with 49 additions and 4 deletions.
  1. +49 −4 docs/topics/db/aggregation.txt
View
53 docs/topics/db/aggregation.txt
@@ -21,14 +21,12 @@ used to track the inventory for a series of online bookstores:
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
- friends = models.ManyToManyField('self', blank=True)
class Publisher(models.Model):
name = models.CharField(max_length=300)
num_awards = models.IntegerField()
class Book(models.Model):
- isbn = models.CharField(max_length=9)
name = models.CharField(max_length=300)
pages = models.IntegerField()
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):
name = models.CharField(max_length=300)
books = models.ManyToManyField(Book)
+ registered_users = models.PositiveIntegerField()
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'))
{'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.
>>> from django.db.models import Count
>>> 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
# 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[0].num_books
1323
@@ -169,7 +170,7 @@ specify the annotation::
Unlike ``aggregate()``, ``annotate()`` is *not* a terminal clause. The output
of the ``annotate()`` clause is a ``QuerySet``; this ``QuerySet`` can be
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
====================
@@ -205,6 +206,50 @@ issue the query::
>>> 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
=======================================
Please sign in to comment.
Something went wrong with that request. Please try again.