From 937314dc05ee06eca91fcbb3ddf4380a60275b59 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 19 May 2020 12:30:49 +0200 Subject: [PATCH] [3.1.x] Fixed #31607 -- Fixed evaluated Subquery equality. Regression in 691def10a0197d83d2d108bd9043b0916d0f09b4. Backport of a125da6a7c79b1d4c55677d0bed6f9b1d7d77353 from master --- django/db/models/expressions.py | 9 ++++++++- tests/expressions/tests.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index eb5fafc90b5..89332699d5f 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1021,11 +1021,18 @@ class Subquery(Expression): def __init__(self, queryset, output_field=None, **extra): self.query = queryset.query self.extra = extra + # Prevent the QuerySet from being evaluated. + self.queryset = queryset._chain(_result_cache=[], prefetch_done=True) super().__init__(output_field) def __getstate__(self): state = super().__getstate__() - state.pop('_constructor_args', None) + args, kwargs = state['_constructor_args'] + if args: + args = (self.queryset, *args[1:]) + else: + kwargs['queryset'] = self.queryset + state['_constructor_args'] = args, kwargs return state def get_source_expressions(self): diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index b17a286d5df..72bca4f2053 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -511,6 +511,25 @@ class BasicExpressionsTests(TestCase): Employee.objects.exclude(company_point_of_contact_set=None).values('pk') ) + def test_subquery_eq(self): + qs = Employee.objects.annotate( + is_ceo=Exists(Company.objects.filter(ceo=OuterRef('pk'))), + is_point_of_contact=Exists( + Company.objects.filter(point_of_contact=OuterRef('pk')), + ), + small_company=Exists( + queryset=Company.objects.filter(num_employees__lt=200), + ), + ).filter(is_ceo=True, is_point_of_contact=False, small_company=True) + self.assertNotEqual( + qs.query.annotations['is_ceo'], + qs.query.annotations['is_point_of_contact'], + ) + self.assertNotEqual( + qs.query.annotations['is_ceo'], + qs.query.annotations['small_company'], + ) + def test_in_subquery(self): # This is a contrived test (and you really wouldn't write this query), # but it is a succinct way to test the __in=Subquery() construct.