mirror of https://github.com/django/django.git
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
This commit is contained in:
parent
58ea6d4561
commit
fb64ea7896
|
@ -698,7 +698,7 @@ class QuerySet(object):
|
||||||
Prepare the query for computing a result that contains aggregate annotations.
|
Prepare the query for computing a result that contains aggregate annotations.
|
||||||
"""
|
"""
|
||||||
opts = self.model._meta
|
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]
|
field_names = [f.attname for f in opts.fields]
|
||||||
self.query.add_fields(field_names, False)
|
self.query.add_fields(field_names, False)
|
||||||
self.query.set_group_by()
|
self.query.set_group_by()
|
||||||
|
@ -736,6 +736,7 @@ class ValuesQuerySet(QuerySet):
|
||||||
aggregate_names = self.query.aggregate_select.keys()
|
aggregate_names = self.query.aggregate_select.keys()
|
||||||
|
|
||||||
names = extra_names + field_names + aggregate_names
|
names = extra_names + field_names + aggregate_names
|
||||||
|
|
||||||
for row in self.query.results_iter():
|
for row in self.query.results_iter():
|
||||||
yield dict(zip(names, row))
|
yield dict(zip(names, row))
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ class BaseQuery(object):
|
||||||
self.tables = [] # Aliases in the order they are created.
|
self.tables = [] # Aliases in the order they are created.
|
||||||
self.where = where()
|
self.where = where()
|
||||||
self.where_class = where
|
self.where_class = where
|
||||||
self.group_by = []
|
self.group_by = None
|
||||||
self.having = where()
|
self.having = where()
|
||||||
self.order_by = []
|
self.order_by = []
|
||||||
self.low_mark, self.high_mark = 0, None # Used for offset/limit
|
self.low_mark, self.high_mark = 0, None # Used for offset/limit
|
||||||
|
@ -177,7 +177,10 @@ class BaseQuery(object):
|
||||||
obj.tables = self.tables[:]
|
obj.tables = self.tables[:]
|
||||||
obj.where = deepcopy(self.where)
|
obj.where = deepcopy(self.where)
|
||||||
obj.where_class = self.where_class
|
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.having = deepcopy(self.having)
|
||||||
obj.order_by = self.order_by[:]
|
obj.order_by = self.order_by[:]
|
||||||
obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
|
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
|
# If there is a group by clause, aggregating does not add useful
|
||||||
# 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:
|
if self.group_by is not None:
|
||||||
from subqueries import AggregateQuery
|
from subqueries import AggregateQuery
|
||||||
query = AggregateQuery(self.model, self.connection)
|
query = AggregateQuery(self.model, self.connection)
|
||||||
|
|
||||||
|
@ -379,8 +382,8 @@ class BaseQuery(object):
|
||||||
result.append('AND')
|
result.append('AND')
|
||||||
result.append(' AND '.join(self.extra_where))
|
result.append(' AND '.join(self.extra_where))
|
||||||
|
|
||||||
if self.group_by:
|
grouping = self.get_grouping()
|
||||||
grouping = self.get_grouping()
|
if grouping:
|
||||||
if ordering:
|
if ordering:
|
||||||
# If the backend can't group by PK (i.e., any database
|
# If the backend can't group by PK (i.e., any database
|
||||||
# other than MySQL), then any fields mentioned in the
|
# other than MySQL), then any fields mentioned in the
|
||||||
|
@ -698,13 +701,15 @@ class BaseQuery(object):
|
||||||
"""
|
"""
|
||||||
qn = self.quote_name_unless_alias
|
qn = self.quote_name_unless_alias
|
||||||
result = []
|
result = []
|
||||||
for col in self.group_by + self.related_select_cols:
|
if self.group_by is not None:
|
||||||
if isinstance(col, (list, tuple)):
|
group_by = self.group_by or []
|
||||||
result.append('%s.%s' % (qn(col[0]), qn(col[1])))
|
for col in group_by + self.related_select_cols + self.extra_select.keys():
|
||||||
elif hasattr(col, 'as_sql'):
|
if isinstance(col, (list, tuple)):
|
||||||
result.append(col.as_sql(qn))
|
result.append('%s.%s' % (qn(col[0]), qn(col[1])))
|
||||||
else:
|
elif hasattr(col, 'as_sql'):
|
||||||
result.append(str(col))
|
result.append(col.as_sql(qn))
|
||||||
|
else:
|
||||||
|
result.append(str(col))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_ordering(self):
|
def get_ordering(self):
|
||||||
|
@ -1224,7 +1229,7 @@ class BaseQuery(object):
|
||||||
# Aggregate references a normal field
|
# Aggregate references a normal field
|
||||||
field_name = field_list[0]
|
field_name = field_list[0]
|
||||||
source = opts.get_field(field_name)
|
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
|
# Only use a column alias if this is a
|
||||||
# standalone aggregate, or an annotation
|
# standalone aggregate, or an annotation
|
||||||
col = (opts.db_table, source.column)
|
col = (opts.db_table, source.column)
|
||||||
|
@ -1816,6 +1821,7 @@ class BaseQuery(object):
|
||||||
primary key, and the query would be equivalent, the optimization
|
primary key, and the query would be equivalent, the optimization
|
||||||
will be made automatically.
|
will be made automatically.
|
||||||
"""
|
"""
|
||||||
|
self.group_by = []
|
||||||
if self.connection.features.allows_group_by_pk:
|
if self.connection.features.allows_group_by_pk:
|
||||||
if len(self.select) == len(self.model._meta.fields):
|
if len(self.select) == len(self.model._meta.fields):
|
||||||
self.group_by.append('.'.join([self.model._meta.db_table,
|
self.group_by.append('.'.join([self.model._meta.db_table,
|
||||||
|
|
|
@ -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])
|
>>> 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')]
|
[(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
|
# Regression for #10199 - Aggregate calls clone the original query so the original query can still be used
|
||||||
>>> books = Book.objects.all()
|
>>> books = Book.objects.all()
|
||||||
>>> _ = books.aggregate(Avg('authors__age'))
|
>>> _ = books.aggregate(Avg('authors__age'))
|
||||||
|
|
Loading…
Reference in New Issue