Skip to content

Commit

Permalink
[1.6.x] Fixed #21126 -- QuerySet value conversion failure
Browse files Browse the repository at this point in the history
A .annotate().select_related() query resulted in misaligned rows vs
columns for compiler.resolve_columns() method.

Report & patch by Michael Manfre.

Backpatch of 83554b0 from master.
  • Loading branch information
akaariai committed Sep 25, 2013
1 parent 5207928 commit d7ae0bc
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 4 deletions.
2 changes: 1 addition & 1 deletion django/db/backends/__init__.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1125,7 +1125,7 @@ def convert_values(self, value, field):
Coerce the value returned by the database backend into a consistent type Coerce the value returned by the database backend into a consistent type
that is compatible with the field type. that is compatible with the field type.
""" """
if value is None: if value is None or field is None:
return value return value
internal_type = field.get_internal_type() internal_type = field.get_internal_type()
if internal_type == 'FloatField': if internal_type == 'FloatField':
Expand Down
12 changes: 9 additions & 3 deletions django/db/models/sql/compiler.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -709,6 +709,10 @@ def results_iter(self):
has_aggregate_select = bool(self.query.aggregate_select) has_aggregate_select = bool(self.query.aggregate_select)
for rows in self.execute_sql(MULTI): for rows in self.execute_sql(MULTI):
for row in rows: for row in rows:
if has_aggregate_select:
loaded_fields = self.query.get_loaded_field_names().get(self.query.model, set()) or self.query.select
aggregate_start = len(self.query.extra_select) + len(loaded_fields)
aggregate_end = aggregate_start + len(self.query.aggregate_select)
if resolve_columns: if resolve_columns:
if fields is None: if fields is None:
# We only set this up here because # We only set this up here because
Expand All @@ -735,12 +739,14 @@ def results_iter(self):
db_table = self.query.get_meta().db_table db_table = self.query.get_meta().db_table
fields = [f for f in fields if db_table in only_load and fields = [f for f in fields if db_table in only_load and
f.column in only_load[db_table]] f.column in only_load[db_table]]
if has_aggregate_select:
# pad None in to fields for aggregates
fields = fields[:aggregate_start] + [
None for x in range(0, aggregate_end - aggregate_start)
] + fields[aggregate_start:]
row = self.resolve_columns(row, fields) row = self.resolve_columns(row, fields)


if has_aggregate_select: if has_aggregate_select:
loaded_fields = self.query.get_loaded_field_names().get(self.query.model, set()) or self.query.select
aggregate_start = len(self.query.extra_select) + len(loaded_fields)
aggregate_end = aggregate_start + len(self.query.aggregate_select)
row = tuple(row[:aggregate_start]) + tuple([ row = tuple(row[:aggregate_start]) + tuple([
self.query.resolve_aggregate(value, aggregate, self.connection) self.query.resolve_aggregate(value, aggregate, self.connection)
for (alias, aggregate), value for (alias, aggregate), value
Expand Down
11 changes: 11 additions & 0 deletions tests/aggregation_regress/tests.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -387,6 +387,17 @@ def test_db_col_table(self):
qs = Entries.objects.annotate(clue_count=Count('clues__ID')) qs = Entries.objects.annotate(clue_count=Count('clues__ID'))
self.assertQuerysetEqual(qs, []) self.assertQuerysetEqual(qs, [])


def test_boolean_conversion(self):
# Aggregates mixed up ordering of columns for backend's convert_values
# method. Refs #21126.
e = Entries.objects.create(Entry='foo')
c = Clues.objects.create(EntryID=e, Clue='bar')
qs = Clues.objects.select_related('EntryID').annotate(Count('ID'))
self.assertQuerysetEqual(
qs, [c], lambda x: x)
self.assertEqual(qs[0].EntryID, e)
self.assertIs(qs[0].EntryID.Exclude, False)

def test_empty(self): def test_empty(self):
# Regression for #10089: Check handling of empty result sets with # Regression for #10089: Check handling of empty result sets with
# aggregates # aggregates
Expand Down

0 comments on commit d7ae0bc

Please sign in to comment.