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:
parent
3e9d2f81c5
commit
4276b5197b
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
try:
|
||||||
query.add_subquery(obj, using)
|
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)
|
||||||
|
try:
|
||||||
obj.add_subquery(subquery, using=using)
|
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]
|
||||||
|
|
|
@ -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']
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue