Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #21126 -- QuerySet value conversion failure

A .annotate().select_related() query resulted in misaligned rows vs
columns for compiler.resolve_columns() method.

Report & patch by Michael Manfre.
  • Loading branch information...
commit 83554b018ef283827c0e7459ab934d447b3419d5 1 parent 42b9feb
Anssi Kääriäinen authored
2  django/db/backends/__init__.py
@@ -1167,7 +1167,7 @@ def convert_values(self, value, field):
1167 1167
         Coerce the value returned by the database backend into a consistent type
1168 1168
         that is compatible with the field type.
1169 1169
         """
1170  
-        if value is None:
  1170
+        if value is None or field is None:
1171 1171
             return value
1172 1172
         internal_type = field.get_internal_type()
1173 1173
         if internal_type == 'FloatField':
12  django/db/models/sql/compiler.py
@@ -686,6 +686,10 @@ def results_iter(self):
686 686
         has_aggregate_select = bool(self.query.aggregate_select)
687 687
         for rows in self.execute_sql(MULTI):
688 688
             for row in rows:
  689
+                if has_aggregate_select:
  690
+                    loaded_fields = self.query.get_loaded_field_names().get(self.query.model, set()) or self.query.select
  691
+                    aggregate_start = len(self.query.extra_select) + len(loaded_fields)
  692
+                    aggregate_end = aggregate_start + len(self.query.aggregate_select)
689 693
                 if resolve_columns:
690 694
                     if fields is None:
691 695
                         # We only set this up here because
@@ -712,12 +716,14 @@ def results_iter(self):
712 716
                             db_table = self.query.get_meta().db_table
713 717
                             fields = [f for f in fields if db_table in only_load and
714 718
                                       f.column in only_load[db_table]]
  719
+                        if has_aggregate_select:
  720
+                            # pad None in to fields for aggregates
  721
+                            fields = fields[:aggregate_start] + [
  722
+                                None for x in range(0, aggregate_end - aggregate_start)
  723
+                            ] + fields[aggregate_start:]
715 724
                     row = self.resolve_columns(row, fields)
716 725
 
717 726
                 if has_aggregate_select:
718  
-                    loaded_fields = self.query.get_loaded_field_names().get(self.query.model, set()) or self.query.select
719  
-                    aggregate_start = len(self.query.extra_select) + len(loaded_fields)
720  
-                    aggregate_end = aggregate_start + len(self.query.aggregate_select)
721 727
                     row = tuple(row[:aggregate_start]) + tuple(
722 728
                         self.query.resolve_aggregate(value, aggregate, self.connection)
723 729
                         for (alias, aggregate), value
11  tests/aggregation_regress/tests.py
@@ -393,6 +393,17 @@ def test_db_col_table(self):
393 393
         qs = Entries.objects.annotate(clue_count=Count('clues__ID'))
394 394
         self.assertQuerysetEqual(qs, [])
395 395
 
  396
+    def test_boolean_conversion(self):
  397
+        # Aggregates mixed up ordering of columns for backend's convert_values
  398
+        # method. Refs #21126.
  399
+        e = Entries.objects.create(Entry='foo')
  400
+        c = Clues.objects.create(EntryID=e, Clue='bar')
  401
+        qs = Clues.objects.select_related('EntryID').annotate(Count('ID'))
  402
+        self.assertQuerysetEqual(
  403
+            qs, [c], lambda x: x)
  404
+        self.assertEqual(qs[0].EntryID, e)
  405
+        self.assertIs(qs[0].EntryID.Exclude, False)
  406
+
396 407
     def test_empty(self):
397 408
         # Regression for #10089: Check handling of empty result sets with
398 409
         # aggregates

0 notes on commit 83554b0

Please sign in to comment.
Something went wrong with that request. Please try again.