diff options
| author | Hasan Ramezani <hasan.r67@gmail.com> | 2020-09-10 14:34:04 +0200 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-09-11 07:58:52 +0200 |
| commit | 5362e08624c6745181944a10979da876934ea136 (patch) | |
| tree | 0e30705c0f6d199986567f0223ffa8928b4d3776 | |
| parent | 84609b3205905097d7d3038d32e6101f012c0619 (diff) | |
Fixed #31943 -- Fixed recreating QuerySet.values()/values_list() when using a pickled Query.
| -rw-r--r-- | django/db/models/query.py | 2 | ||||
| -rw-r--r-- | docs/ref/models/querysets.txt | 14 | ||||
| -rw-r--r-- | tests/queryset_pickle/tests.py | 24 |
3 files changed, 39 insertions, 1 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py index b48d0df9c0..85cd8311a7 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -210,6 +210,8 @@ class QuerySet: @query.setter def query(self, value): + if value.values_select: + self._iterable_class = ValuesIterable self._query = value def as_manager(cls): diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index 228e2cf736..7f55684e08 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -106,6 +106,20 @@ the query construction and is not part of the public API. However, it is safe (and fully supported) to pickle and unpickle the attribute's contents as described here. +.. admonition:: Restrictions on ``QuerySet.values_list()`` + + If you recreate :meth:`QuerySet.values_list` using the pickled ``query`` + attribute, it will be converted to :meth:`QuerySet.values`:: + + >>> import pickle + >>> qs = Blog.objects.values_list('id', 'name') + >>> qs + <QuerySet [(1, 'Beatles Blog')]> + >>> reloaded_qs = Blog.objects.all() + >>> reloaded_qs.query = pickle.loads(pickle.dumps(qs.query)) + >>> reloaded_qs + <QuerySet [{'id': 1, 'name': 'Beatles Blog'}]> + .. admonition:: You can't share pickles between versions Pickles of ``QuerySets`` are only valid for the version of Django that diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index a58e45caed..8575c6fe81 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -11,7 +11,7 @@ from .models import Container, Event, Group, Happening, M2MModel, MyEvent class PickleabilityTestCase(TestCase): @classmethod def setUpTestData(cls): - Happening.objects.create() # make sure the defaults are working (#20158) + cls.happening = Happening.objects.create() # make sure the defaults are working (#20158) def assert_pickles(self, qs): self.assertEqual(list(pickle.loads(pickle.dumps(qs))), list(qs)) @@ -224,6 +224,28 @@ class PickleabilityTestCase(TestCase): qs = Happening.objects.annotate(latest_time=models.Max('when')) self.assert_pickles(qs) + def test_annotation_values(self): + qs = Happening.objects.values('name').annotate(latest_time=models.Max('when')) + reloaded = Happening.objects.all() + reloaded.query = pickle.loads(pickle.dumps(qs.query)) + self.assertEqual( + reloaded.get(), + {'name': 'test', 'latest_time': self.happening.when}, + ) + + def test_annotation_values_list(self): + # values_list() is reloaded to values() when using a pickled query. + tests = [ + Happening.objects.values_list('name'), + Happening.objects.values_list('name', flat=True), + Happening.objects.values_list('name', named=True), + ] + for qs in tests: + with self.subTest(qs._iterable_class.__name__): + reloaded = Happening.objects.all() + reloaded.query = pickle.loads(pickle.dumps(qs.query)) + self.assertEqual(reloaded.get(), {'name': 'test'}) + def test_filter_deferred(self): qs = Happening.objects.all() qs._defer_next_filter = True |
