mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[1.8.x] Fixed #24508 -- Made annotations commutative
Backport of 127b3873d0 from master
			
			
This commit is contained in:
		| @@ -238,7 +238,16 @@ class BaseExpression(object): | |||||||
|         """ |         """ | ||||||
|         Attempts to infer the output type of the expression. If the output |         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 |         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: |         if self._output_field is None: | ||||||
|             sources = self.get_source_fields() |             sources = self.get_source_fields() | ||||||
| @@ -246,8 +255,9 @@ class BaseExpression(object): | |||||||
|             if num_sources == 0: |             if num_sources == 0: | ||||||
|                 self._output_field = None |                 self._output_field = None | ||||||
|             else: |             else: | ||||||
|                 self._output_field = sources[0] |  | ||||||
|                 for source in sources: |                 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__): |                     if source is not None and not isinstance(self._output_field, source.__class__): | ||||||
|                         raise FieldError( |                         raise FieldError( | ||||||
|                             "Expression contains mixed types. You must set output_field") |                             "Expression contains mixed types. You must set output_field") | ||||||
|   | |||||||
| @@ -99,6 +99,14 @@ class NonAggregateAnnotationTestCase(TestCase): | |||||||
|                 sum_rating=Sum('rating') |                 sum_rating=Sum('rating') | ||||||
|             ).filter(sum_rating=F('nope'))) |             ).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): |     def test_update_with_annotation(self): | ||||||
|         book_preupdate = Book.objects.get(pk=2) |         book_preupdate = Book.objects.get(pk=2) | ||||||
|         Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating')) |         Book.objects.annotate(other_rating=F('rating') - 1).update(rating=F('other_rating')) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user