summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJani Tiainen <jani.tiainen@tintti.net>2016-08-31 21:16:39 +0300
committerTim Graham <timograham@gmail.com>2016-09-22 19:59:11 -0400
commit7a2c27112d1f804f75191e9bf45a96a89318a684 (patch)
tree5b56b563a31e282eeed4f653100c8b9036e1a1c0
parent78ec4dfeffccb08fe4c2314d5f044c147ada30c9 (diff)
Fixed #27159 -- Prevented pickling a query with an __in=inner_qs lookup from evaluating inner_qs.
-rw-r--r--django/db/models/fields/related_lookups.py19
-rw-r--r--tests/queryset_pickle/tests.py39
2 files changed, 58 insertions, 0 deletions
diff --git a/django/db/models/fields/related_lookups.py b/django/db/models/fields/related_lookups.py
index de2564fb0d..9d4baa6d03 100644
--- a/django/db/models/fields/related_lookups.py
+++ b/django/db/models/fields/related_lookups.py
@@ -83,6 +83,25 @@ class RelatedIn(In):
else:
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):
def get_prep_lookup(self):
diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py
index 3c24f204c9..4a28e58da5 100644
--- a/tests/queryset_pickle/tests.py
+++ b/tests/queryset_pickle/tests.py
@@ -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()
with self.assertRaisesMessage(RuntimeWarning, msg):
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])