mirror of https://github.com/django/django.git
Fixed #31843 -- Fixed pickling named values from QuerySet.values_list().
This commit is contained in:
parent
3a9f192b13
commit
981a072dd4
1
AUTHORS
1
AUTHORS
|
@ -934,6 +934,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
Vincent Foley <vfoleybourgon@yahoo.ca>
|
Vincent Foley <vfoleybourgon@yahoo.ca>
|
||||||
Vinny Do <vdo.code@gmail.com>
|
Vinny Do <vdo.code@gmail.com>
|
||||||
Vitaly Babiy <vbabiy86@gmail.com>
|
Vitaly Babiy <vbabiy86@gmail.com>
|
||||||
|
Vitaliy Yelnik <velnik@gmail.com>
|
||||||
Vladimir Kuzma <vladimirkuzma.ch@gmail.com>
|
Vladimir Kuzma <vladimirkuzma.ch@gmail.com>
|
||||||
Vlado <vlado@labath.org>
|
Vlado <vlado@labath.org>
|
||||||
Vsevolod Solovyov
|
Vsevolod Solovyov
|
||||||
|
|
|
@ -5,8 +5,6 @@ The main QuerySet implementation. This provides the public API for the ORM.
|
||||||
import copy
|
import copy
|
||||||
import operator
|
import operator
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
|
||||||
from functools import lru_cache
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
import django
|
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.functions import Cast, Trunc
|
||||||
from django.db.models.query_utils import FilteredRelation, Q
|
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.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 import timezone
|
||||||
from django.utils.functional import cached_property, partition
|
from django.utils.functional import cached_property, partition
|
||||||
|
|
||||||
|
@ -148,13 +146,6 @@ class NamedValuesListIterable(ValuesListIterable):
|
||||||
namedtuple for each row.
|
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):
|
def __iter__(self):
|
||||||
queryset = self.queryset
|
queryset = self.queryset
|
||||||
if queryset._fields:
|
if queryset._fields:
|
||||||
|
@ -162,7 +153,7 @@ class NamedValuesListIterable(ValuesListIterable):
|
||||||
else:
|
else:
|
||||||
query = queryset.query
|
query = queryset.query
|
||||||
names = [*query.extra_select, *query.values_select, *query.annotation_select]
|
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__
|
new = tuple.__new__
|
||||||
for row in super().__iter__():
|
for row in super().__iter__():
|
||||||
yield new(tuple_class, row)
|
yield new(tuple_class, row)
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
import functools
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
def make_model_tuple(model):
|
def make_model_tuple(model):
|
||||||
"""
|
"""
|
||||||
Take a model or a string of the form "app_label.ModelName" and return a
|
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():
|
for k, v in mapping.items():
|
||||||
yield k, v() if callable(v) else v
|
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__})
|
||||||
|
|
|
@ -2408,6 +2408,11 @@ class ValuesQuerysetTests(TestCase):
|
||||||
values = qs.first()
|
values = qs.first()
|
||||||
self.assertEqual(values._fields, ('combinedexpression2', 'combinedexpression1'))
|
self.assertEqual(values._fields, ('combinedexpression2', 'combinedexpression1'))
|
||||||
|
|
||||||
|
def test_named_values_pickle(self):
|
||||||
|
value = Number.objects.values_list('num', 'other_num', named=True).get()
|
||||||
|
self.assertEqual(value, (72, None))
|
||||||
|
self.assertEqual(pickle.loads(pickle.dumps(value)), value)
|
||||||
|
|
||||||
|
|
||||||
class QuerySetSupportsPythonIdioms(TestCase):
|
class QuerySetSupportsPythonIdioms(TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue