﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
30727	Pickling a QuerySet evaluates the querysets given to Subquery in annotate.	Andrew Brown	Andrew Brown	"I wrote a test case for `tests/queryset_pickle/tests.py` modeled after the test from bug #27499 which is very similar.

{{{#!python
    def test_pickle_subquery_queryset_not_evaluated(self):
        """"""
        Verifies that querysets passed into Subquery expressions
        are not evaluated when pickled
        """"""
        groups = Group.objects.annotate(
            has_event=models.Exists(Event.objects.filter(group_id=models.OuterRef('id')))
        )
        with self.assertNumQueries(0):
            pickle.loads(pickle.dumps(groups.query))
}}}

The Subquery class (which is the base for `Exists`) only stores the underlying query object and throws the QuerySet away (as of this [https://github.com/django/django/commit/96b6ad94d9ebbd57b77b44e185ee215b5b899ac8 commit], although I don't think it worked before that). So in theory it shouldn't be pickling the QuerySet.

However, the QuerySet is still stored on the instance within the `_constructor_args` attribute added by the `@deconstructible` decorator on the `BaseExpression` base class. So when the inner query object gets pickled, so does the QuerySet, which attempts to evaluate it. In this case, it gets the error ""ValueError: This queryset contains a reference to an outer query and may only be used in a subquery."" since the inner queryset is being evaluated independently when called from pickle: it calls `QuerySet.__getstate__()`.

I'm not sure what the best solution is here. I made a patch that adds the following override to `__getstate__` to the SubQuery class, which fixes the problem and passes all other Django tests on my machine. I'll submit a PR shortly, but welcome any better approaches since I'm not sure what else that may effect.

{{{#!python
class Subquery(Expression):
    ...
    def __getstate__(self):
        obj_dict = super().__getstate__()
        obj_dict.pop('_constructor_args', None)
        return obj_dict
}}}"	Bug	closed	Database layer (models, ORM)	dev	Normal	fixed			Accepted	1	0	0	0	0	0
