summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorKwist <velnik@gmail.com>2020-08-31 11:54:34 +0300
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-09-16 13:43:06 +0200
commit981a072dd4dec586f8fc606712ed9a2ef116eeee (patch)
tree177faf3614530a902f8c11bd7eec787bb3241274 /django
parent3a9f192b131f7a9b0fe5783c684b23015fa67cc8 (diff)
Fixed #31843 -- Fixed pickling named values from QuerySet.values_list().
Diffstat (limited to 'django')
-rw-r--r--django/db/models/query.py13
-rw-r--r--django/db/models/utils.py18
2 files changed, 20 insertions, 11 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 67ffe7f000..6c78fbc4b3 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -5,8 +5,6 @@ The main QuerySet implementation. This provides the public API for the ORM.
import copy
import operator
import warnings
-from collections import namedtuple
-from functools import lru_cache
from itertools import chain
import django
@@ -23,7 +21,7 @@ from django.db.models.expressions import Case, Expression, F, Value, When
from django.db.models.functions import Cast, Trunc
from django.db.models.query_utils import FilteredRelation, Q
from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE
-from django.db.models.utils import resolve_callables
+from django.db.models.utils import create_namedtuple_class, resolve_callables
from django.utils import timezone
from django.utils.functional import cached_property, partition
@@ -148,13 +146,6 @@ class NamedValuesListIterable(ValuesListIterable):
namedtuple for each row.
"""
- @staticmethod
- @lru_cache()
- def create_namedtuple_class(*names):
- # Cache namedtuple() with @lru_cache() since it's too slow to be
- # called for every QuerySet evaluation.
- return namedtuple('Row', names)
-
def __iter__(self):
queryset = self.queryset
if queryset._fields:
@@ -162,7 +153,7 @@ class NamedValuesListIterable(ValuesListIterable):
else:
query = queryset.query
names = [*query.extra_select, *query.values_select, *query.annotation_select]
- tuple_class = self.create_namedtuple_class(*names)
+ tuple_class = create_namedtuple_class(*names)
new = tuple.__new__
for row in super().__iter__():
yield new(tuple_class, row)
diff --git a/django/db/models/utils.py b/django/db/models/utils.py
index 989667dc8c..764ca5888b 100644
--- a/django/db/models/utils.py
+++ b/django/db/models/utils.py
@@ -1,3 +1,7 @@
+import functools
+from collections import namedtuple
+
+
def make_model_tuple(model):
"""
Take a model or a string of the form "app_label.ModelName" and return a
@@ -28,3 +32,17 @@ def resolve_callables(mapping):
"""
for k, v in mapping.items():
yield k, v() if callable(v) else v
+
+
+def unpickle_named_row(names, values):
+ return create_namedtuple_class(*names)(*values)
+
+
+@functools.lru_cache()
+def create_namedtuple_class(*names):
+ # Cache type() with @lru_cache() since it's too slow to be called for every
+ # QuerySet evaluation.
+ def __reduce__(self):
+ return unpickle_named_row, (names, tuple(self))
+
+ return type('Row', (namedtuple('Row', names),), {'__reduce__': __reduce__})