Fixed #30727 -- Made Subquery pickle without evaluating their QuerySet.
Subquery expression objects, when pickled, were evaluating the QuerySet objects saved in its _constructor_args attribute.
This commit is contained in:
parent
03dbdfd9bb
commit
691def10a0
|
@ -1019,6 +1019,11 @@ class Subquery(Expression):
|
||||||
self.extra = extra
|
self.extra = extra
|
||||||
super().__init__(output_field)
|
super().__init__(output_field)
|
||||||
|
|
||||||
|
def __getstate__(self):
|
||||||
|
state = super().__getstate__()
|
||||||
|
state.pop('_constructor_args', None)
|
||||||
|
return state
|
||||||
|
|
||||||
def get_source_expressions(self):
|
def get_source_expressions(self):
|
||||||
return [self.query]
|
return [self.query]
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,41 @@ class PickleabilityTestCase(TestCase):
|
||||||
m2ms = pickle.loads(pickle.dumps(m2ms))
|
m2ms = pickle.loads(pickle.dumps(m2ms))
|
||||||
self.assertSequenceEqual(m2ms, [m2m])
|
self.assertSequenceEqual(m2ms, [m2m])
|
||||||
|
|
||||||
|
def test_pickle_exists_queryset_still_usable(self):
|
||||||
|
group = Group.objects.create(name='group')
|
||||||
|
Event.objects.create(title='event', group=group)
|
||||||
|
groups = Group.objects.annotate(
|
||||||
|
has_event=models.Exists(
|
||||||
|
Event.objects.filter(group_id=models.OuterRef('id')),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
groups2 = pickle.loads(pickle.dumps(groups))
|
||||||
|
self.assertSequenceEqual(groups2.filter(has_event=True), [group])
|
||||||
|
|
||||||
|
def test_pickle_exists_queryset_not_evaluated(self):
|
||||||
|
group = Group.objects.create(name='group')
|
||||||
|
Event.objects.create(title='event', group=group)
|
||||||
|
groups = Group.objects.annotate(
|
||||||
|
has_event=models.Exists(
|
||||||
|
Event.objects.filter(group_id=models.OuterRef('id')),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
list(groups) # evaluate QuerySet.
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
self.assert_pickles(groups)
|
||||||
|
|
||||||
|
def test_pickle_subquery_queryset_not_evaluated(self):
|
||||||
|
group = Group.objects.create(name='group')
|
||||||
|
Event.objects.create(title='event', group=group)
|
||||||
|
groups = Group.objects.annotate(
|
||||||
|
event_title=models.Subquery(
|
||||||
|
Event.objects.filter(group_id=models.OuterRef('id')).values('title'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
list(groups) # evaluate QuerySet.
|
||||||
|
with self.assertNumQueries(0):
|
||||||
|
self.assert_pickles(groups)
|
||||||
|
|
||||||
def test_annotation_with_callable_default(self):
|
def test_annotation_with_callable_default(self):
|
||||||
# Happening.when has a callable default of datetime.datetime.now.
|
# Happening.when has a callable default of datetime.datetime.now.
|
||||||
qs = Happening.objects.annotate(latest_time=models.Max('when'))
|
qs = Happening.objects.annotate(latest_time=models.Max('when'))
|
||||||
|
|
Loading…
Reference in New Issue