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.
|
| 3 | 3 | """ |
| 4 | 4 | |
| 5 | 5 | import copy |
| | 6 | from collections import namedtuple |
| 6 | 7 | import itertools |
| 7 | 8 | import sys |
| 8 | 9 | |
| … |
… |
class ValuesQuerySet(QuerySet):
|
| 1056 | 1057 | |
| 1057 | 1058 | |
| 1058 | 1059 | class 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 | |
| 1059 | 1070 | def iterator(self): |
| 1060 | 1071 | if self.flat and len(self._fields) == 1: |
| 1061 | 1072 | for row in self.query.get_compiler(self.db).results_iter(): |
| 1062 | 1073 | yield row[0] |
| 1063 | 1074 | elif not self.query.extra_select and not self.query.aggregate_select: |
| | 1075 | ret_tuple = self.cached_tuple(self.field_names) |
| 1064 | 1076 | for row in self.query.get_compiler(self.db).results_iter(): |
| 1065 | | yield tuple(row) |
| | 1077 | yield ret_tuple(*row) |
| 1066 | 1078 | else: |
| 1067 | 1079 | # When extra(select=...) or an annotation is involved, the extra |
| 1068 | 1080 | # cols are always at the start of the row, and we need to reorder |
| … |
… |
class ValuesListQuerySet(ValuesQuerySet):
|
| 1080 | 1092 | else: |
| 1081 | 1093 | fields = names |
| 1082 | 1094 | |
| | 1095 | ret_tuple = self.cached_tuple(fields) |
| 1083 | 1096 | 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]) |
| 1086 | 1099 | |
| 1087 | 1100 | def _clone(self, *args, **kwargs): |
| 1088 | 1101 | clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs) |
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):
|
| 213 | 213 | |
| 214 | 214 | # Values list works the same way |
| 215 | 215 | # 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()) |
| 216 | 217 | self.assertEqual( |
| 217 | | list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), |
| | 218 | lst, |
| 218 | 219 | [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] |
| 219 | 220 | ) |
| | 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') |
| 220 | 226 | |
| 221 | 227 | # 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'))))) |
| 222 | 229 | self.assertEqual( |
| 223 | | list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), |
| | 230 | lst, |
| 224 | 231 | [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] |
| 225 | 232 | ) |
| | 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') |
| 226 | 238 | |
| 227 | 239 | # 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')) |
| 228 | 241 | 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')] |
| 231 | 243 | ) |
| | 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') |
| 232 | 247 | |
| 233 | 248 | # Extra columns after a non-empty values_list() clause are ignored completely |
| 234 | 249 | self.assertEqual( |
diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py
index ded3e8f..03dc9fe 100644
|
a
|
b
|
class ValuesQuerysetTests(BaseQuerysetTest):
|
| 1639 | 1639 | qs, [72] |
| 1640 | 1640 | ) |
| 1641 | 1641 | |
| | 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 | |
| 1642 | 1648 | |
| 1643 | 1649 | class WeirdQuerysetSlicingTests(BaseQuerysetTest): |
| 1644 | 1650 | def setUp(self): |