Ticket #15648: values_list_namedtuples.diff

File values_list_namedtuples.diff, 5.1 KB (added by Anssi Kääriäinen, 12 years ago)
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 44acadf..ea7c282 100644
    a b The main QuerySet implementation. This provides the public API for the ORM.  
    33"""
    44
    55import copy
     6from collections import namedtuple
    67import itertools
    78import sys
    89
    class ValuesQuerySet(QuerySet):  
    10561057
    10571058
    10581059class ValuesListQuerySet(ValuesQuerySet):
     1060    namedtuple_cache = {} # A map of fieldnames -> tuple to use
     1061    def cached_tuple(self, names):
     1062        names = tuple(names)
     1063        try:
     1064            return self.namedtuple_cache[names]
     1065        except KeyError:
     1066            self.namedtuple_cache[names] = namedtuple('ValuesListRow',
     1067                                                     ' '.join(names))
     1068            return self.namedtuple_cache[names]
     1069
    10591070    def iterator(self):
    10601071        if self.flat and len(self._fields) == 1:
    10611072            for row in self.query.get_compiler(self.db).results_iter():
    10621073                yield row[0]
    10631074        elif not self.query.extra_select and not self.query.aggregate_select:
     1075            ret_tuple = self.cached_tuple(self.field_names)
    10641076            for row in self.query.get_compiler(self.db).results_iter():
    1065                 yield tuple(row)
     1077                yield ret_tuple(*row)
    10661078        else:
    10671079            # When extra(select=...) or an annotation is involved, the extra
    10681080            # cols are always at the start of the row, and we need to reorder
    class ValuesListQuerySet(ValuesQuerySet):  
    10801092            else:
    10811093                fields = names
    10821094
     1095            ret_tuple = self.cached_tuple(fields)
    10831096            for row in self.query.get_compiler(self.db).results_iter():
    1084                 data = dict(zip(names, row))
    1085                 yield tuple([data[f] for f in fields])
     1097                data = dict(zip(names, row)) 
     1098                yield ret_tuple(*[data[k] for k in fields])
    10861099
    10871100    def _clone(self, *args, **kwargs):
    10881101        clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs)
  • tests/regressiontests/extra_regress/tests.py

    diff --git a/tests/regressiontests/extra_regress/tests.py b/tests/regressiontests/extra_regress/tests.py
    index 67efb42..c2962dc 100644
    a b class ExtraRegressTests(TestCase):  
    213213
    214214        # Values list works the same way
    215215        # All columns are returned for an empty values_list()
     216        lst = list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list())
    216217        self.assertEqual(
    217             list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()),
     218            lst,
    218219            [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')]
    219220        )
     221        # Check that the namedtuple names are correctly assigned
     222        self.assertEqual(lst[0].foo, u'first')
     223        self.assertEqual(lst[0].bar, u'second')
     224        self.assertEqual(lst[0].id, obj.pk)
     225        self.assertEqual(lst[0].third, u'third')
    220226
    221227        # Extra columns after an empty values_list() are still included
     228        lst = list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))))
    222229        self.assertEqual(
    223             list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))),
     230            lst,
    224231            [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')]
    225232        )
     233        # Check that the namedtuple names are correctly assigned
     234        self.assertEqual(lst[0].foo, u'first')
     235        self.assertEqual(lst[0].bar, u'second')
     236        self.assertEqual(lst[0].id, obj.pk)
     237        self.assertEqual(lst[0].third, u'third')
    226238
    227239        # Extra columns ignored completely if not mentioned in values_list()
     240        lst = list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second'))
    228241        self.assertEqual(
    229             list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')),
    230             [(u'first', u'second')]
     242            lst, [(u'first', u'second')]
    231243        )
     244        # Check that the namedtuple names are correctly assigned
     245        self.assertEqual(lst[0].first, u'first')
     246        self.assertEqual(lst[0].second, u'second')
    232247
    233248        # Extra columns after a non-empty values_list() clause are ignored completely
    234249        self.assertEqual(
  • tests/regressiontests/queries/tests.py

    diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py
    index ded3e8f..03dc9fe 100644
    a b class ValuesQuerysetTests(BaseQuerysetTest):  
    16391639            qs, [72]
    16401640        )
    16411641
     1642    def test_values_list_namedtuples(self):
     1643        Number.objects.create(num=72)
     1644        qs = Number.objects.values_list("num")
     1645        self.assertEqual(qs[0][0], 72)
     1646        self.assertEqual(qs[0].num, 72)
     1647
    16421648
    16431649class WeirdQuerysetSlicingTests(BaseQuerysetTest):
    16441650    def setUp(self):
Back to Top