Loading django/db/models/expressions.py +12 −2 Original line number Diff line number Diff line Loading @@ -238,7 +238,16 @@ class BaseExpression(object): """ 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() Loading @@ -246,8 +255,9 @@ class BaseExpression(object): 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") Loading tests/annotations/tests.py +8 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,14 @@ class NonAggregateAnnotationTestCase(TestCase): 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=2) Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating')) Loading Loading
django/db/models/expressions.py +12 −2 Original line number Diff line number Diff line Loading @@ -238,7 +238,16 @@ class BaseExpression(object): """ 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() Loading @@ -246,8 +255,9 @@ class BaseExpression(object): 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") Loading
tests/annotations/tests.py +8 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,14 @@ class NonAggregateAnnotationTestCase(TestCase): 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=2) Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating')) Loading