mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[5.2.x] Fixed #35235 -- Removed caching of BaseExpression._output_field_or_none.
Backport of cbb0812683 from main.
			
			
This commit is contained in:
		| @@ -167,13 +167,16 @@ class Combinable: | ||||
|         return NegatedExpression(self) | ||||
|  | ||||
|  | ||||
| class OutputFieldIsNoneError(FieldError): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class BaseExpression: | ||||
|     """Base class for all query expressions.""" | ||||
|  | ||||
|     empty_result_set_value = NotImplemented | ||||
|     # aggregate specific fields | ||||
|     is_summary = False | ||||
|     _output_field_resolved_to_none = False | ||||
|     # Can the expression be used in a WHERE clause? | ||||
|     filterable = True | ||||
|     # Can the expression be used as a source expression in Window? | ||||
| @@ -323,11 +326,12 @@ class BaseExpression: | ||||
|         """Return the output type of this expressions.""" | ||||
|         output_field = self._resolve_output_field() | ||||
|         if output_field is None: | ||||
|             self._output_field_resolved_to_none = True | ||||
|             raise FieldError("Cannot resolve expression type, unknown output_field") | ||||
|             raise OutputFieldIsNoneError( | ||||
|                 "Cannot resolve expression type, unknown output_field" | ||||
|             ) | ||||
|         return output_field | ||||
|  | ||||
|     @cached_property | ||||
|     @property | ||||
|     def _output_field_or_none(self): | ||||
|         """ | ||||
|         Return the output field of this expression, or None if | ||||
| @@ -335,9 +339,8 @@ class BaseExpression: | ||||
|         """ | ||||
|         try: | ||||
|             return self.output_field | ||||
|         except FieldError: | ||||
|             if not self._output_field_resolved_to_none: | ||||
|                 raise | ||||
|         except OutputFieldIsNoneError: | ||||
|             return | ||||
|  | ||||
|     def _resolve_output_field(self): | ||||
|         """ | ||||
|   | ||||
| @@ -51,6 +51,7 @@ from django.db.models.expressions import ( | ||||
|     Combinable, | ||||
|     CombinedExpression, | ||||
|     NegatedExpression, | ||||
|     OutputFieldIsNoneError, | ||||
|     RawSQL, | ||||
|     Ref, | ||||
| ) | ||||
| @@ -2329,6 +2330,16 @@ class ValueTests(TestCase): | ||||
|         time = Time.objects.annotate(one=Value(1, output_field=DecimalField())).first() | ||||
|         self.assertEqual(time.one, 1) | ||||
|  | ||||
|     def test_output_field_is_none_error(self): | ||||
|         with self.assertRaises(OutputFieldIsNoneError): | ||||
|             Employee.objects.annotate(custom_expression=Value(None)).first() | ||||
|  | ||||
|     def test_output_field_or_none_property_not_cached(self): | ||||
|         expression = Value(None, output_field=None) | ||||
|         self.assertIsNone(expression._output_field_or_none) | ||||
|         expression.output_field = BooleanField() | ||||
|         self.assertIsInstance(expression._output_field_or_none, BooleanField) | ||||
|  | ||||
|     def test_resolve_output_field(self): | ||||
|         value_types = [ | ||||
|             ("str", CharField), | ||||
|   | ||||
| @@ -334,6 +334,22 @@ class TestGeneralAggregate(PostgreSQLTestCase): | ||||
|         ) | ||||
|         self.assertCountEqual(qs, [[], [5]]) | ||||
|  | ||||
|     def test_array_agg_with_empty_filter_and_default_values(self): | ||||
|         for filter_value in ([-1], []): | ||||
|             for default_value in ([], Value([])): | ||||
|                 with self.subTest(filter=filter_value, default=default_value): | ||||
|                     queryset = AggregateTestModel.objects.annotate( | ||||
|                         test_array_agg=ArrayAgg( | ||||
|                             "stattestmodel__int1", | ||||
|                             filter=Q(pk__in=filter_value), | ||||
|                             default=default_value, | ||||
|                         ) | ||||
|                     ) | ||||
|                     self.assertSequenceEqual( | ||||
|                         queryset.values_list("test_array_agg", flat=True), | ||||
|                         [[], [], [], []], | ||||
|                     ) | ||||
|  | ||||
|     def test_bit_and_general(self): | ||||
|         values = AggregateTestModel.objects.filter(integer_field__in=[0, 1]).aggregate( | ||||
|             bitand=BitAnd("integer_field") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user