Fixed #12687 -- fixed an issue with aggregates and counts in conjunction with annotations where the QuerySet was provably empty.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14586 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2010-11-17 02:57:14 +00:00
parent 3e9d2f81c5
commit 4276b5197b
4 changed files with 36 additions and 4 deletions

View File

@ -8,6 +8,7 @@ class AggregateField(object):
""" """
def __init__(self, internal_type): def __init__(self, internal_type):
self.internal_type = internal_type self.internal_type = internal_type
def get_internal_type(self): def get_internal_type(self):
return self.internal_type return self.internal_type

View File

@ -337,7 +337,7 @@ class Query(object):
# information but retrieves only the first row. Aggregate # information but retrieves only the first row. Aggregate
# over the subquery instead. # over the subquery instead.
if self.group_by is not None: if self.group_by is not None:
from subqueries import AggregateQuery from django.db.models.sql.subqueries import AggregateQuery
query = AggregateQuery(self.model) query = AggregateQuery(self.model)
obj = self.clone() obj = self.clone()
@ -349,7 +349,13 @@ class Query(object):
query.aggregate_select[alias] = aggregate query.aggregate_select[alias] = aggregate
del obj.aggregate_select[alias] del obj.aggregate_select[alias]
query.add_subquery(obj, using) try:
query.add_subquery(obj, using)
except EmptyResultSet:
return dict(
(alias, None)
for alias in query.aggregate_select
)
else: else:
query = self query = self
self.select = [] self.select = []
@ -382,13 +388,19 @@ class Query(object):
# If a select clause exists, then the query has already started to # If a select clause exists, then the query has already started to
# specify the columns that are to be returned. # specify the columns that are to be returned.
# In this case, we need to use a subquery to evaluate the count. # In this case, we need to use a subquery to evaluate the count.
from subqueries import AggregateQuery from django.db.models.sql.subqueries import AggregateQuery
subquery = obj subquery = obj
subquery.clear_ordering(True) subquery.clear_ordering(True)
subquery.clear_limits() subquery.clear_limits()
obj = AggregateQuery(obj.model) obj = AggregateQuery(obj.model)
obj.add_subquery(subquery, using=using) try:
obj.add_subquery(subquery, using=using)
except EmptyResultSet:
# add_subquery evaluates the query, if it's an EmptyResultSet
# then there are can be no results, and therefore there the
# count is obviously 0
return 0
obj.add_count_column() obj.add_count_column()
number = obj.get_aggregation(using=using)[None] number = obj.get_aggregation(using=using)[None]

View File

@ -11,6 +11,7 @@ from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.query import Query from django.db.models.sql.query import Query
from django.db.models.sql.where import AND, Constraint from django.db.models.sql.where import AND, Constraint
__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery', __all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery',
'AggregateQuery'] 'AggregateQuery']

View File

@ -622,6 +622,24 @@ class AggregationTests(TestCase):
lambda: Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age')) lambda: Book.objects.annotate(mean_age=Avg('authors__age')).annotate(Avg('mean_age'))
) )
def test_empty_filter_count(self):
self.assertEqual(
Author.objects.filter(id__in=[]).annotate(Count("friends")).count(),
0
)
def test_empty_filter_aggregate(self):
self.assertEqual(
Author.objects.filter(id__in=[]).annotate(Count("friends")).aggregate(Count("pk")),
{"pk__count": None}
)
def test_annotate_and_join(self):
self.assertEqual(
Author.objects.annotate(c=Count("friends__name")).exclude(friends__name="Joe").count(),
Author.objects.count()
)
@skipUnlessDBFeature('supports_stddev') @skipUnlessDBFeature('supports_stddev')
def test_stddev(self): def test_stddev(self):
self.assertEqual( self.assertEqual(