Commits

Anonymous committed 503dc90

Fixed #10089 -- Corrected handling of aggregates when the query set contains no items (and the cursor returns None). Thanks to Kyle Fox for the report, and david for the initial patch.

Comments (0)

Files changed (5)

django/db/models/sql/query.py

         to return Decimal and long types when they are not needed.
         """
         if value is None:
+            if aggregate.is_ordinal:
+                return 0
             # Return None as-is
             return value
         elif aggregate.is_ordinal:
         query.related_select_cols = []
         query.related_select_fields = []
 
+        result = query.execute_sql(SINGLE)
+        if result is None:
+            result = [None for q in query.aggregate_select.items()]
+
         return dict([
             (alias, self.resolve_aggregate(val, aggregate))
             for (alias, aggregate), val
-            in zip(query.aggregate_select.items(), query.execute_sql(SINGLE))
+            in zip(query.aggregate_select.items(), result)
         ])
 
     def get_count(self):

tests/modeltests/aggregation/fixtures/initial_data.json

         }
     },
     {
+        "pk": 5,
+        "model": "aggregation.publisher",
+        "fields": {
+            "name": "Jonno's House of Books",
+            "num_awards": 0
+        }
+    },
+    {
         "pk": 1,
         "model": "aggregation.book",
         "fields": {

tests/modeltests/aggregation/models.py

 # Annotate each publisher with the sum of the price of all books sold
 >>> publishers = Publisher.objects.all().annotate(Sum('book__price'))
 >>> sorted([(p.name, p.book__price__sum) for p in publishers])
-[(u'Apress', Decimal("59.69")), (u'Morgan Kaufmann', Decimal("75.00")), (u'Prentice Hall', Decimal("112.49")), (u'Sams', Decimal("23.09"))]
+[(u'Apress', Decimal("59.69")), (u"Jonno's House of Books", None), (u'Morgan Kaufmann', Decimal("75.00")), (u'Prentice Hall', Decimal("112.49")), (u'Sams', Decimal("23.09"))]
 
 # Calls to values() are not commutative over annotate().
 
 []
 
 # Aggregates also work on dates, times and datetimes
->>> Publisher.objects.annotate(earliest_book=Min('book__pubdate')).order_by('earliest_book').values()
+>>> Publisher.objects.annotate(earliest_book=Min('book__pubdate')).exclude(earliest_book=None).order_by('earliest_book').values()
 [{'earliest_book': datetime.date(1991, 10, 15), 'num_awards': 9, 'id': 4, 'name': u'Morgan Kaufmann'}, {'earliest_book': datetime.date(1995, 1, 15), 'num_awards': 7, 'id': 3, 'name': u'Prentice Hall'}, {'earliest_book': datetime.date(2007, 12, 6), 'num_awards': 3, 'id': 1, 'name': u'Apress'}, {'earliest_book': datetime.date(2008, 3, 3), 'num_awards': 1, 'id': 2, 'name': u'Sams'}]
 
 >>> Store.objects.aggregate(Max('friday_night_closing'), Min("original_opening"))

tests/regressiontests/aggregation_regress/fixtures/initial_data.json

         }
     },
     {
+        "pk": 5,
+        "model": "aggregation_regress.publisher",
+        "fields": {
+            "name": "Jonno's House of Books",
+            "num_awards": 0
+        }
+    },
+    {
         "pk": 1,
         "model": "aggregation_regress.book",
         "fields": {

tests/regressiontests/aggregation_regress/models.py

 >>> len(Book.objects.annotate(num_authors=Count('authors')).exclude(num_authors__lt=2).filter(num_authors__lt=3))
 2
 
+# Regression for #10089: Check handling of empty result sets with aggregates
+>>> Book.objects.filter(id__in=[]).count()
+0
+
+>>> Book.objects.filter(id__in=[]).aggregate(num_authors=Count('authors'), avg_authors=Avg('authors'), max_authors=Max('authors'), max_price=Max('price'), max_rating=Max('rating'))
+{'max_authors': None, 'max_rating': None, 'num_authors': 0, 'avg_authors': None, 'max_price': None}
+
+>>> Publisher.objects.filter(pk=5).annotate(num_authors=Count('book__authors'), avg_authors=Avg('book__authors'), max_authors=Max('book__authors'), max_price=Max('book__price'), max_rating=Max('book__rating')).values()
+[{'max_authors': None, 'name': u"Jonno's House of Books", 'num_awards': 0, 'max_price': None, 'num_authors': 0, 'max_rating': None, 'id': 5, 'avg_authors': None}]
+
 """
 }