Commit 127b3873 authored by Josh Smeaton's avatar Josh Smeaton
Browse files

Fixed #24508 -- Made annotations commutative

parent a6bada1e
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -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()
@@ -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")
+8 −0
Original line number Diff line number Diff line
@@ -182,6 +182,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=self.b2.pk)
        Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating'))