summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Keith-Magee <russell@keith-magee.com>2009-02-16 12:29:31 +0000
committerRussell Keith-Magee <russell@keith-magee.com>2009-02-16 12:29:31 +0000
commitfb64ea78968714929a75514ecd55fb5af1093697 (patch)
tree2b98a4447d342c8d138eb17a7f9a419e8ce5ef83
parent58ea6d45618cb8b5bd4306c395d7bb8860f9a842 (diff)
Fixed #10132 -- Corrected the interaction of extra() queries with the values() clause. Thanks to Glen Maynard for the report.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@9838 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/models/query.py3
-rw-r--r--django/db/models/sql/query.py32
-rw-r--r--tests/regressiontests/aggregation_regress/models.py7
3 files changed, 28 insertions, 14 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 86fafc519d..5b1f4e66fd 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -698,7 +698,7 @@ class QuerySet(object):
Prepare the query for computing a result that contains aggregate annotations.
"""
opts = self.model._meta
- if not self.query.group_by:
+ if self.query.group_by is None:
field_names = [f.attname for f in opts.fields]
self.query.add_fields(field_names, False)
self.query.set_group_by()
@@ -736,6 +736,7 @@ class ValuesQuerySet(QuerySet):
aggregate_names = self.query.aggregate_select.keys()
names = extra_names + field_names + aggregate_names
+
for row in self.query.results_iter():
yield dict(zip(names, row))
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index bf664c606e..f868347960 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -68,7 +68,7 @@ class BaseQuery(object):
self.tables = [] # Aliases in the order they are created.
self.where = where()
self.where_class = where
- self.group_by = []
+ self.group_by = None
self.having = where()
self.order_by = []
self.low_mark, self.high_mark = 0, None # Used for offset/limit
@@ -177,7 +177,10 @@ class BaseQuery(object):
obj.tables = self.tables[:]
obj.where = deepcopy(self.where)
obj.where_class = self.where_class
- obj.group_by = self.group_by[:]
+ if self.group_by is None:
+ obj.group_by = None
+ else:
+ obj.group_by = self.group_by[:]
obj.having = deepcopy(self.having)
obj.order_by = self.order_by[:]
obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
@@ -272,7 +275,7 @@ class BaseQuery(object):
# If there is a group by clause, aggregating does not add useful
# information but retrieves only the first row. Aggregate
# over the subquery instead.
- if self.group_by:
+ if self.group_by is not None:
from subqueries import AggregateQuery
query = AggregateQuery(self.model, self.connection)
@@ -379,8 +382,8 @@ class BaseQuery(object):
result.append('AND')
result.append(' AND '.join(self.extra_where))
- if self.group_by:
- grouping = self.get_grouping()
+ grouping = self.get_grouping()
+ if grouping:
if ordering:
# If the backend can't group by PK (i.e., any database
# other than MySQL), then any fields mentioned in the
@@ -698,13 +701,15 @@ class BaseQuery(object):
"""
qn = self.quote_name_unless_alias
result = []
- for col in self.group_by + self.related_select_cols:
- if isinstance(col, (list, tuple)):
- result.append('%s.%s' % (qn(col[0]), qn(col[1])))
- elif hasattr(col, 'as_sql'):
- result.append(col.as_sql(qn))
- else:
- result.append(str(col))
+ if self.group_by is not None:
+ group_by = self.group_by or []
+ for col in group_by + self.related_select_cols + self.extra_select.keys():
+ if isinstance(col, (list, tuple)):
+ result.append('%s.%s' % (qn(col[0]), qn(col[1])))
+ elif hasattr(col, 'as_sql'):
+ result.append(col.as_sql(qn))
+ else:
+ result.append(str(col))
return result
def get_ordering(self):
@@ -1224,7 +1229,7 @@ class BaseQuery(object):
# Aggregate references a normal field
field_name = field_list[0]
source = opts.get_field(field_name)
- if not (self.group_by and is_summary):
+ if not (self.group_by is not None and is_summary):
# Only use a column alias if this is a
# standalone aggregate, or an annotation
col = (opts.db_table, source.column)
@@ -1816,6 +1821,7 @@ class BaseQuery(object):
primary key, and the query would be equivalent, the optimization
will be made automatically.
"""
+ self.group_by = []
if self.connection.features.allows_group_by_pk:
if len(self.select) == len(self.model._meta.fields):
self.group_by.append('.'.join([self.model._meta.db_table,
diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py
index 8eac67a0fc..51648dada6 100644
--- a/tests/regressiontests/aggregation_regress/models.py
+++ b/tests/regressiontests/aggregation_regress/models.py
@@ -200,6 +200,13 @@ FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, conta
>>> sorted([(b.name, b.authors__age__avg, b.publisher.name, b.contact.name) for b in books])
[(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')]
+# Regression for #10132 - If the values() clause only mentioned extra(select=) columns, those columns are used for grouping
+>>> Book.objects.extra(select={'pub':'publisher_id'}).values('pub').annotate(Count('id')).order_by('pub')
+[{'pub': 1, 'id__count': 2}, {'pub': 2, 'id__count': 1}, {'pub': 3, 'id__count': 2}, {'pub': 4, 'id__count': 1}]
+
+>>> Book.objects.extra(select={'pub':'publisher_id','foo':'pages'}).values('pub').annotate(Count('id')).order_by('pub')
+[{'pub': 1, 'id__count': 2}, {'pub': 2, 'id__count': 1}, {'pub': 3, 'id__count': 2}, {'pub': 4, 'id__count': 1}]
+
# Regression for #10199 - Aggregate calls clone the original query so the original query can still be used
>>> books = Book.objects.all()
>>> _ = books.aggregate(Avg('authors__age'))