Permalink
Browse files

Fixed #24508 -- Made annotations commutative

  • Loading branch information...
1 parent a6bada1 commit 127b3873d03704f77428b984de022664b268314e @jarshwah jarshwah committed Mar 20, 2015
Showing with 20 additions and 2 deletions.
  1. +12 −2 django/db/models/expressions.py
  2. +8 −0 tests/annotations/tests.py
@@ -238,16 +238,26 @@ def _resolve_output_field(self):
"""
Attempts to infer the output type of the expression. If the output
fields of all source fields match then we can simply infer the same
- type here.
+ type here. This isn't always correct, but it makes sense most of the
+ time.
+
+ Consider the difference between `2 + 2` and `2 / 3`. Inferring
+ the type here is a convenience for the common case. The user should
+ supply their own output_field with more complex computations.
+
+ If a source does not have an `_output_field` then we exclude it from
+ this check. If all sources are `None`, then an error will be thrown
+ higher up the stack in the `output_field` property.
"""
if self._output_field is None:
sources = self.get_source_fields()
num_sources = len(sources)
if num_sources == 0:
self._output_field = None
else:
- self._output_field = sources[0]
for source in sources:
+ if self._output_field is None:
+ self._output_field = source
if source is not None and not isinstance(self._output_field, source.__class__):
raise FieldError(
"Expression contains mixed types. You must set output_field")
@@ -182,6 +182,14 @@ def test_filter_wrong_annotation(self):
sum_rating=Sum('rating')
).filter(sum_rating=F('nope')))
+ def test_combined_annotation_commutative(self):
+ book1 = Book.objects.annotate(adjusted_rating=F('rating') + 2).get(pk=self.b1.pk)
+ book2 = Book.objects.annotate(adjusted_rating=2 + F('rating')).get(pk=self.b1.pk)
+ self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
+ book1 = Book.objects.annotate(adjusted_rating=F('rating') + None).get(pk=self.b1.pk)
+ book2 = Book.objects.annotate(adjusted_rating=None + F('rating')).get(pk=self.b1.pk)
+ self.assertEqual(book1.adjusted_rating, book2.adjusted_rating)
+
def test_update_with_annotation(self):
book_preupdate = Book.objects.get(pk=self.b2.pk)
Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating'))

0 comments on commit 127b387

Please sign in to comment.