Fixed #27159 -- Prevented pickling a query with an __in=inner_qs lookup from evaluating inner_qs.

This commit is contained in:
Jani Tiainen 2016-08-31 21:16:39 +03:00 committed by Tim Graham
parent 78ec4dfeff
commit 7a2c27112d
2 changed files with 58 additions and 0 deletions

View File

@ -83,6 +83,25 @@ class RelatedIn(In):
else: else:
return super(RelatedIn, self).as_sql(compiler, connection) return super(RelatedIn, self).as_sql(compiler, connection)
def __getstate__(self):
"""
Prevent pickling a query with an __in=inner_qs lookup from evaluating
inner_qs.
"""
from django.db.models.query import QuerySet # Avoid circular import
state = self.__dict__.copy()
if isinstance(self.rhs, QuerySet):
state['rhs'] = (self.rhs.__class__, self.rhs.query)
return state
def __setstate__(self, state):
self.__dict__.update(state)
if isinstance(self.rhs, tuple):
queryset_class, query = self.rhs
queryset = queryset_class()
queryset.query = query
self.rhs = queryset
class RelatedLookupMixin(object): class RelatedLookupMixin(object):
def get_prep_lookup(self): def get_prep_lookup(self):

View File

@ -153,3 +153,42 @@ class PickleabilityTestCase(TestCase):
msg = "Pickled queryset instance's Django version 1.0 does not match the current version %s." % get_version() msg = "Pickled queryset instance's Django version 1.0 does not match the current version %s." % get_version()
with self.assertRaisesMessage(RuntimeWarning, msg): with self.assertRaisesMessage(RuntimeWarning, msg):
pickle.loads(pickle.dumps(qs)) pickle.loads(pickle.dumps(qs))
class InLookupTests(TestCase):
@classmethod
def setUpTestData(cls):
for i in range(1, 3):
group = Group.objects.create(name='Group {}'.format(i))
cls.e1 = Event.objects.create(title='Event 1', group=group)
def test_in_lookup_queryset_evaluation(self):
"""
Neither pickling nor unpickling a QuerySet.query with an __in=inner_qs
lookup should evaluate inner_qs.
"""
events = Event.objects.filter(group__in=Group.objects.all())
with self.assertNumQueries(0):
dumped = pickle.dumps(events.query)
with self.assertNumQueries(0):
reloaded = pickle.loads(dumped)
reloaded_events = Event.objects.none()
reloaded_events.query = reloaded
self.assertSequenceEqual(reloaded_events, [self.e1])
def test_in_lookup_query_evaluation(self):
events = Event.objects.filter(group__in=Group.objects.values('id').query)
with self.assertNumQueries(0):
dumped = pickle.dumps(events.query)
with self.assertNumQueries(0):
reloaded = pickle.loads(dumped)
reloaded_events = Event.objects.none()
reloaded_events.query = reloaded
self.assertSequenceEqual(reloaded_events, [self.e1])