Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.5.x] Mention backward relationships in aggregate docs.

Thanks Anssi and Marc Tamlyn for reviewing.

Fixes #19803.

0560bfb from master.
  • Loading branch information...
commit 89ce55b301d163500b99c72bfefd9f07c5542091 1 parent e7ab0d3
Ramiro Morales authored February 11, 2013

Showing 1 changed file with 49 additions and 4 deletions. Show diff stats Hide diff stats

  1. 53  docs/topics/db/aggregation.txt
53  docs/topics/db/aggregation.txt
@@ -21,14 +21,12 @@ used to track the inventory for a series of online bookstores:
21 21
     class Author(models.Model):
22 22
        name = models.CharField(max_length=100)
23 23
        age = models.IntegerField()
24  
-       friends = models.ManyToManyField('self', blank=True)
25 24
 
26 25
     class Publisher(models.Model):
27 26
        name = models.CharField(max_length=300)
28 27
        num_awards = models.IntegerField()
29 28
 
30 29
     class Book(models.Model):
31  
-       isbn = models.CharField(max_length=9)
32 30
        name = models.CharField(max_length=300)
33 31
        pages = models.IntegerField()
34 32
        price = models.DecimalField(max_digits=10, decimal_places=2)
@@ -40,6 +38,7 @@ used to track the inventory for a series of online bookstores:
40 38
     class Store(models.Model):
41 39
        name = models.CharField(max_length=300)
42 40
        books = models.ManyToManyField(Book)
  41
+       registered_users = models.PositiveIntegerField()
43 42
 
44 43
 Cheat sheet
45 44
 ===========
@@ -64,6 +63,9 @@ In a hurry? Here's how to do common aggregate queries, assuming the models above
64 63
     >>> Book.objects.all().aggregate(Max('price'))
65 64
     {'price__max': Decimal('81.20')}
66 65
 
  66
+    # All the following queries involve traversing the Book<->Publisher
  67
+    # many-to-many relationship backward
  68
+
67 69
     # Each publisher, each with a count of books as a "num_books" attribute.
68 70
     >>> from django.db.models import Count
69 71
     >>> 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 75
     73
74 76
 
75 77
     # The top 5 publishers, in order by number of books.
76  
-    >>> from django.db.models import Count
77 78
     >>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
78 79
     >>> pubs[0].num_books
79 80
     1323
@@ -169,7 +170,7 @@ specify the annotation::
169 170
 Unlike ``aggregate()``, ``annotate()`` is *not* a terminal clause. The output
170 171
 of the ``annotate()`` clause is a ``QuerySet``; this ``QuerySet`` can be
171 172
 modified using any other ``QuerySet`` operation, including ``filter()``,
172  
-``order_by``, or even additional calls to ``annotate()``.
  173
+``order_by()``, or even additional calls to ``annotate()``.
173 174
 
174 175
 Joins and aggregates
175 176
 ====================
@@ -205,6 +206,50 @@ issue the query::
205 206
 
206 207
     >>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))
207 208
 
  209
+Following relationships backwards
  210
+---------------------------------
  211
+
  212
+In a way similar to :ref:`lookups-that-span-relationships`, aggregations and
  213
+annotations on fields of models or models that are related to the one you are
  214
+querying can include traversing "reverse" relationships. The lowercase name
  215
+of related models and double-underscores are used here too.
  216
+
  217
+For example, we can ask for all publishers, annotated with their respective
  218
+total book stock counters (note how we use `'book'` to specify the
  219
+Publisher->Book reverse foreign key hop)::
  220
+
  221
+    >>> from django.db.models import Count, Min, Sum, Max, Avg
  222
+    >>> Publisher.objects.annotate(Count('book'))
  223
+
  224
+(Every Publisher in the resulting QuerySet will have an extra attribute called
  225
+``book__count``.)
  226
+
  227
+We can also ask for the oldest book of any of those managed by every publisher::
  228
+
  229
+    >>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))
  230
+
  231
+(The resulting dictionary will have a key called ``'oldest_pubdate'``. If no
  232
+such alias was specified, it would be the rather long ``'book__pubdate__min'``.)
  233
+
  234
+This doesn't apply just to foreign keys. It also works with many-to-many
  235
+relations. For example, we can ask for every author, annotated with the total
  236
+number of pages considering all the books he/she has (co-)authored (note how we
  237
+use `'book'` to specify the Author->Book reverse many-to-many hop)::
  238
+
  239
+    >>> Author.objects.annotate(total_pages=Sum('book__pages'))
  240
+
  241
+(Every Author in the resulting QuerySet will have an extra attribute called
  242
+``total_pages``. If no such alias was specified, it would be the rather long
  243
+``book__pages__sum``.)
  244
+
  245
+Or ask for the average rating of all the books written by author(s) we have on
  246
+file::
  247
+
  248
+    >>> Author.objects.aggregate(average_rating=Avg('book__rating'))
  249
+
  250
+(The resulting dictionary will have a key called ``'average__rating'``. If no
  251
+such alias was specified, it would be the rather long ``'book__rating__avg'``.)
  252
+
208 253
 Aggregations and other QuerySet clauses
209 254
 =======================================
210 255
 

0 notes on commit 89ce55b

Please sign in to comment.
Something went wrong with that request. Please try again.