Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #10127 -- Corrected (no, really, this time!) the way the select…

…_related() cache is populated when annotations are also contained in the query. Thanks to Sylvain Pasche <sylvain.pasche@gmail.com> for the report and test case.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9808 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit ecadf675690c2cc9375e9af70ebd1528841eae76 1 parent a008b5c
Russell Keith-Magee authored February 03, 2009
6  django/db/models/query.py
@@ -281,7 +281,8 @@ def iterator(self):
281 281
         for row in self.query.results_iter():
282 282
             if fill_cache:
283 283
                 obj, _ = get_cached_row(self.model, row,
284  
-                                        index_start, max_depth, requested=requested)
  284
+                            index_start, max_depth,
  285
+                            requested=requested, offset=len(aggregate_select))
285 286
             else:
286 287
                 # omit aggregates in object creation
287 288
                 obj = self.model(*row[index_start:aggregate_start])
@@ -898,7 +899,7 @@ def iterator(self):
898 899
 
899 900
 
900 901
 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
901  
-                   requested=None):
  902
+                   requested=None, offset=0):
902 903
     """
903 904
     Helper function that recursively returns an object with the specified
904 905
     related attributes already populated.
@@ -915,6 +916,7 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
915 916
         obj = None
916 917
     else:
917 918
         obj = klass(*fields)
  919
+    index_end += offset
918 920
     for f in klass._meta.fields:
919 921
         if not select_related_descend(f, restricted, requested):
920 922
             continue
8  tests/modeltests/aggregation/fixtures/initial_data.json
@@ -49,6 +49,7 @@
49 49
             "price": "30.00",
50 50
             "rating": 4.5,
51 51
             "authors": [1, 2],
  52
+            "contact": 1,
52 53
             "pages": 447,
53 54
             "pubdate": "2007-12-6"
54 55
         }
@@ -63,6 +64,7 @@
63 64
             "price": "23.09",
64 65
             "rating": 3.0,
65 66
             "authors": [3],
  67
+            "contact": 3,
66 68
             "pages": 528,
67 69
             "pubdate": "2008-3-3"
68 70
         }
@@ -77,6 +79,7 @@
77 79
             "price": "29.69",
78 80
             "rating": 4.0,
79 81
             "authors": [4],
  82
+            "contact": 4,
80 83
             "pages": 300,
81 84
             "pubdate": "2008-6-23"
82 85
         }
@@ -91,6 +94,7 @@
91 94
             "price": "29.69",
92 95
             "rating": 4.0,
93 96
             "authors": [5, 6, 7],
  97
+            "contact": 5,
94 98
             "pages": 350,
95 99
             "pubdate": "2008-11-3"
96 100
         }
@@ -105,6 +109,7 @@
105 109
             "price": "82.80",
106 110
             "rating": 4.0,
107 111
             "authors": [8, 9],
  112
+            "contact": 8,
108 113
             "pages": 1132,
109 114
             "pubdate": "1995-1-15"
110 115
         }
@@ -119,6 +124,7 @@
119 124
             "price": "75.00",
120 125
             "rating": 5.0,
121 126
             "authors": [8],
  127
+            "contact": 8,
122 128
             "pages": 946,
123 129
             "pubdate": "1991-10-15"
124 130
         }
@@ -195,7 +201,7 @@
195 201
         "fields": {
196 202
             "age": 37,
197 203
             "friends": [6, 7],
198  
-            "name": "Jeffrey Forcier "
  204
+            "name": "Jeffrey Forcier"
199 205
         }
200 206
     },
201 207
     {
15  tests/modeltests/aggregation/models.py
@@ -28,6 +28,7 @@ class Book(models.Model):
28 28
    rating = models.FloatField()
29 29
    price = models.DecimalField(decimal_places=2, max_digits=6)
30 30
    authors = models.ManyToManyField(Author)
  31
+   contact = models.ForeignKey(Author, related_name='book_contact_set')
31 32
    publisher = models.ForeignKey(Publisher)
32 33
    pubdate = models.DateField()
33 34
 
@@ -180,7 +181,7 @@ class Clues(models.Model):
180 181
 # Count the number of books written by each author
181 182
 >>> authors = Author.objects.annotate(num_books=Count('book'))
182 183
 >>> sorted([(a.name, a.num_books) for a in authors])
183  
-[(u'Adrian Holovaty', 1), (u'Brad Dayley', 1), (u'Jacob Kaplan-Moss', 1), (u'James Bennett', 1), (u'Jeffrey Forcier ', 1), (u'Paul Bissex', 1), (u'Peter Norvig', 2), (u'Stuart Russell', 1), (u'Wesley J. Chun', 1)]
  184
+[(u'Adrian Holovaty', 1), (u'Brad Dayley', 1), (u'Jacob Kaplan-Moss', 1), (u'James Bennett', 1), (u'Jeffrey Forcier', 1), (u'Paul Bissex', 1), (u'Peter Norvig', 2), (u'Stuart Russell', 1), (u'Wesley J. Chun', 1)]
184 185
 
185 186
 # On OneToMany Relationships
186 187
 
@@ -201,7 +202,7 @@ class Clues(models.Model):
201 202
 # Calling values on a queryset that has annotations returns the output
202 203
 # as a dictionary
203 204
 >>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values()
204  
-[{'rating': 4.5, 'isbn': u'159059725', 'name': u'The Definitive Guide to Django: Web Development Done Right', 'pubdate': datetime.date(2007, 12, 6), 'price': Decimal("30..."), 'id': 1, 'publisher_id': 1, 'pages': 447, 'mean_age': 34.5}]
  205
+[{'rating': 4.5, 'isbn': u'159059725', 'name': u'The Definitive Guide to Django: Web Development Done Right', 'pubdate': datetime.date(2007, 12, 6), 'price': Decimal("30..."), 'contact_id': 1, 'id': 1, 'publisher_id': 1, 'pages': 447, 'mean_age': 34.5}]
205 206
 
206 207
 >>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('pk', 'isbn', 'mean_age')
207 208
 [{'pk': 1, 'isbn': u'159059725', 'mean_age': 34.5}]
@@ -214,7 +215,7 @@ class Clues(models.Model):
214 215
 # An empty values() call before annotating has the same effect as an
215 216
 # empty values() call after annotating
216 217
 >>> Book.objects.filter(pk=1).values().annotate(mean_age=Avg('authors__age'))
217  
-[{'rating': 4.5, 'isbn': u'159059725', 'name': u'The Definitive Guide to Django: Web Development Done Right', 'pubdate': datetime.date(2007, 12, 6), 'price': Decimal("30..."), 'id': 1, 'publisher_id': 1, 'pages': 447, 'mean_age': 34.5}]
  218
+[{'rating': 4.5, 'isbn': u'159059725', 'name': u'The Definitive Guide to Django: Web Development Done Right', 'pubdate': datetime.date(2007, 12, 6), 'price': Decimal("30..."), 'contact_id': 1, 'id': 1, 'publisher_id': 1, 'pages': 447, 'mean_age': 34.5}]
218 219
 
219 220
 # Calling annotate() on a ValuesQuerySet annotates over the groups of
220 221
 # fields to be selected by the ValuesQuerySet.
@@ -231,7 +232,7 @@ class Clues(models.Model):
231 232
 >>> len(authors)
232 233
 9
233 234
 >>> sorted([(a.name, a.friends__age__avg) for a in authors])
234  
-[(u'Adrian Holovaty', 32.0), (u'Brad Dayley', None), (u'Jacob Kaplan-Moss', 29.5), (u'James Bennett', 34.0), (u'Jeffrey Forcier ', 27.0), (u'Paul Bissex', 31.0), (u'Peter Norvig', 46.0), (u'Stuart Russell', 57.0), (u'Wesley J. Chun', 33.6...)]
  235
+[(u'Adrian Holovaty', 32.0), (u'Brad Dayley', None), (u'Jacob Kaplan-Moss', 29.5), (u'James Bennett', 34.0), (u'Jeffrey Forcier', 27.0), (u'Paul Bissex', 31.0), (u'Peter Norvig', 46.0), (u'Stuart Russell', 57.0), (u'Wesley J. Chun', 33.6...)]
235 236
 
236 237
 
237 238
 # The Count aggregation function allows an extra parameter: distinct.
@@ -268,9 +269,9 @@ class Clues(models.Model):
268 269
 # Lets add a publisher to test the different possibilities for filtering
269 270
 >>> p = Publisher(name='Expensive Publisher', num_awards=0)
270 271
 >>> p.save()
271  
->>> Book(name='ExpensiveBook1', pages=1, isbn='111', rating=3.5, price=Decimal("1000"), publisher=p, pubdate=date(2008,12,1)).save()
272  
->>> Book(name='ExpensiveBook2', pages=1, isbn='222', rating=4.0, price=Decimal("1000"), publisher=p, pubdate=date(2008,12,2)).save()
273  
->>> Book(name='ExpensiveBook3', pages=1, isbn='333', rating=4.5, price=Decimal("35"), publisher=p, pubdate=date(2008,12,3)).save()
  272
+>>> Book(name='ExpensiveBook1', pages=1, isbn='111', rating=3.5, price=Decimal("1000"), publisher=p, contact_id=1, pubdate=date(2008,12,1)).save()
  273
+>>> Book(name='ExpensiveBook2', pages=1, isbn='222', rating=4.0, price=Decimal("1000"), publisher=p, contact_id=1, pubdate=date(2008,12,2)).save()
  274
+>>> Book(name='ExpensiveBook3', pages=1, isbn='333', rating=4.5, price=Decimal("35"), publisher=p, contact_id=1, pubdate=date(2008,12,3)).save()
274 275
 
275 276
 # Publishers that have:
276 277
 
8  tests/regressiontests/aggregation_regress/fixtures/initial_data.json
@@ -49,6 +49,7 @@
49 49
             "price": "30.00",
50 50
             "rating": 4.5,
51 51
             "authors": [1, 2],
  52
+            "contact": 1,
52 53
             "pages": 447,
53 54
             "pubdate": "2007-12-6"
54 55
         }
@@ -63,6 +64,7 @@
63 64
             "price": "23.09",
64 65
             "rating": 3.0,
65 66
             "authors": [3],
  67
+            "contact": 3,
66 68
             "pages": 528,
67 69
             "pubdate": "2008-3-3"
68 70
         }
@@ -77,6 +79,7 @@
77 79
             "price": "29.69",
78 80
             "rating": 4.0,
79 81
             "authors": [4],
  82
+            "contact": 4,
80 83
             "pages": 300,
81 84
             "pubdate": "2008-6-23"
82 85
         }
@@ -91,6 +94,7 @@
91 94
             "price": "29.69",
92 95
             "rating": 4.0,
93 96
             "authors": [5, 6, 7],
  97
+            "contact": 5,
94 98
             "pages": 350,
95 99
             "pubdate": "2008-11-3"
96 100
         }
@@ -105,6 +109,7 @@
105 109
             "price": "82.80",
106 110
             "rating": 4.0,
107 111
             "authors": [8, 9],
  112
+            "contact": 8,
108 113
             "pages": 1132,
109 114
             "pubdate": "1995-1-15"
110 115
         }
@@ -119,6 +124,7 @@
119 124
             "price": "75.00",
120 125
             "rating": 5.0,
121 126
             "authors": [8],
  127
+            "contact": 8,
122 128
             "pages": 946,
123 129
             "pubdate": "1991-10-15"
124 130
         }
@@ -195,7 +201,7 @@
195 201
         "fields": {
196 202
             "age": 37,
197 203
             "friends": [6, 7],
198  
-            "name": "Jeffrey Forcier "
  204
+            "name": "Jeffrey Forcier"
199 205
         }
200 206
     },
201 207
     {
21  tests/regressiontests/aggregation_regress/models.py
@@ -29,6 +29,7 @@ class Book(models.Model):
29 29
    rating = models.FloatField()
30 30
    price = models.DecimalField(decimal_places=2, max_digits=6)
31 31
    authors = models.ManyToManyField(Author)
  32
+   contact = models.ForeignKey(Author, related_name='book_contact_set')
32 33
    publisher = models.ForeignKey(Publisher)
33 34
    pubdate = models.DateField()
34 35
 
@@ -80,19 +81,19 @@ def __unicode__(self):
80 81
 
81 82
 # Annotations get combined with extra select clauses
82 83
 >>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).__dict__.items())
83  
-[('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
  84
+[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
84 85
 
85 86
 # Order of the annotate/extra in the query doesn't matter
86 87
 >>> sorted(Book.objects.all().extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2).__dict__.items())
87  
-[('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
  88
+[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
88 89
 
89 90
 # Values queries can be combined with annotate and extra
90 91
 >>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2).items())
91  
-[('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
  92
+[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
92 93
 
93 94
 # The order of the values, annotate and extra clauses doesn't matter
94 95
 >>> sorted(Book.objects.all().values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).items())
95  
-[('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
  96
+[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
96 97
 
97 98
 # A values query that selects specific columns reduces the output
98 99
 >>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'price_per_page' : 'price / pages'}).values('name').get(pk=1).items())
@@ -119,17 +120,17 @@ def __unicode__(self):
119 120
 >>> Book.objects.all().aggregate(num_authors=Count('foo'))
120 121
 Traceback (most recent call last):
121 122
 ...
122  
-FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, id, isbn, name, pages, price, pubdate, publisher, rating, store
  123
+FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, id, isbn, name, pages, price, pubdate, publisher, rating, store
123 124
 
124 125
 >>> Book.objects.all().annotate(num_authors=Count('foo'))
125 126
 Traceback (most recent call last):
126 127
 ...
127  
-FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, id, isbn, name, pages, price, pubdate, publisher, rating, store
  128
+FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, id, isbn, name, pages, price, pubdate, publisher, rating, store
128 129
 
129 130
 >>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Max('foo'))
130 131
 Traceback (most recent call last):
131 132
 ...
132  
-FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, id, isbn, name, pages, price, pubdate, publisher, rating, store, num_authors
  133
+FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, contact, id, isbn, name, pages, price, pubdate, publisher, rating, store, num_authors
133 134
 
134 135
 # Old-style count aggregations can be mixed with new-style
135 136
 >>> Book.objects.annotate(num_authors=Count('authors')).count()
@@ -149,7 +150,7 @@ def __unicode__(self):
149 150
 
150 151
 # Regression for #10064: select_related() plays nice with aggregates
151 152
 >>> Book.objects.select_related('publisher').annotate(num_authors=Count('authors')).values()[0]
152  
-{'rating': 4.0, 'isbn': u'013790395', 'name': u'Artificial Intelligence: A Modern Approach', 'pubdate': datetime.date(1995, 1, 15), 'price': Decimal("82.8..."), 'id': 5, 'num_authors': 2, 'publisher_id': 3, 'pages': 1132}
  153
+{'rating': 4.0, 'isbn': u'013790395', 'name': u'Artificial Intelligence: A Modern Approach', 'pubdate': datetime.date(1995, 1, 15), 'price': Decimal("82.8..."), 'contact_id': 8, 'id': 5, 'num_authors': 2, 'publisher_id': 3, 'pages': 1132}
153 154
 
154 155
 # Regression for #10010: exclude on an aggregate field is correctly negated
155 156
 >>> len(Book.objects.annotate(num_authors=Count('authors')))
@@ -196,8 +197,8 @@ def __unicode__(self):
196 197
 
197 198
 # Regression for #10127 - Empty select_related() works with annotate
198 199
 >>> books = Book.objects.all().filter(rating__lt=4.5).select_related().annotate(Avg('authors__age'))
199  
->>> sorted([(b.name, b.authors__age__avg) for b in books])
200  
-[(u'Artificial Intelligence: A Modern Approach', 51.5), (u'Practical Django Projects', 29.0), (u'Python Web Development with Django', 30.3...), (u'Sams Teach Yourself Django in 24 Hours', 45.0)]
  200
+>>> sorted([(b.name, b.authors__age__avg, b.publisher.name, b.contact.name) for b in books])
  201
+[(u'Artificial Intelligence: A Modern Approach', 51.5, u'Prentice Hall', u'Peter Norvig'), (u'Practical Django Projects', 29.0, u'Apress', u'James Bennett'), (u'Python Web Development with Django', 30.3..., u'Prentice Hall', u'Jeffrey Forcier'), (u'Sams Teach Yourself Django in 24 Hours', 45.0, u'Sams', u'Brad Dayley')]
201 202
 
202 203
 """
203 204
 }

0 notes on commit ecadf67

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