Commit fb64ea78 authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

Fixed #10132 -- Corrected the interaction of extra() queries with the values()...

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
parent 58ea6d45
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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))

+19 −13
Original line number Diff line number Diff line
@@ -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,6 +177,9 @@ class BaseQuery(object):
        obj.tables = self.tables[:]
        obj.where = deepcopy(self.where)
        obj.where_class = self.where_class
        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[:]
@@ -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()
        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,7 +701,9 @@ class BaseQuery(object):
        """
        qn = self.quote_name_unless_alias
        result = []
        for col in self.group_by + self.related_select_cols:
        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'):
@@ -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,
+7 −0
Original line number Diff line number Diff line
@@ -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'))