Code

Ticket #2482: valuelist.diff

File valuelist.diff, 4.0 KB (added by mccutchen@…, 8 years ago)

Patch which implements valuelist() method

Line 
1Index: django/db/models/query.py
2===================================================================
3--- django/db/models/query.py   (revision 3557)
4+++ django/db/models/query.py   (working copy)
5@@ -308,6 +308,9 @@
6     def values(self, *fields):
7         return self._clone(klass=ValuesQuerySet, _fields=fields)
8 
9+    def valuelist(self, *fields):
10+        return self._clone(klass=ValueListQuerySet, _fields=fields)
11+
12     def dates(self, field_name, kind, order='ASC'):
13         """
14         Returns a list of datetime objects representing all available dates
15@@ -509,23 +512,30 @@
16         return select, " ".join(sql), params
17 
18 class ValuesQuerySet(QuerySet):
19-    def iterator(self):
20+    def get_columns(self):
21+        if self._fields:
22+            return [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
23+        else:
24+            return [f.column for f in self.model._meta.fields]
25+
26+    def get_field_names(self):
27+        return self._fields and self._fields or [f.attname for f in self.model._meta.fields]
28+
29+    def get_values_cursor(self):
30         # select_related and select aren't supported in values().
31         self._select_related = False
32         self._select = {}
33 
34-        # self._fields is a list of field names to fetch.
35-        if self._fields:
36-            columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
37-            field_names = self._fields
38-        else: # Default to all fields.
39-            columns = [f.column for f in self.model._meta.fields]
40-            field_names = [f.attname for f in self.model._meta.fields]
41-
42+        columns = self.get_columns()
43         cursor = connection.cursor()
44         select, sql, params = self._get_sql_clause()
45         select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
46         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
47+        return cursor
48+
49+    def iterator(self):
50+        cursor = self.get_values_cursor()
51+        field_names = self.get_field_names()
52         while 1:
53             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
54             if not rows:
55@@ -538,6 +548,24 @@
56         c._fields = self._fields[:]
57         return c
58 
59+class ValueListQuerySet(ValuesQuerySet):
60+    def iterator(self):
61+        cursor = self.get_values_cursor()
62+        while 1:
63+            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
64+            if not rows:
65+                raise StopIteration
66+            for row in rows:
67+                if len(row) == 1:
68+                    yield row[0]
69+                else:
70+                    yield tuple(row)
71+
72+    def _clone(self, klass=None, **kwargs):
73+        c = super(ValueListQuerySet, self)._clone(klass, **kwargs)
74+        c._fields = self._fields[:]
75+        return c
76+
77 class DateQuerySet(QuerySet):
78     def iterator(self):
79         from django.db.backends.util import typecast_timestamp
80Index: tests/modeltests/lookup/models.py
81===================================================================
82--- tests/modeltests/lookup/models.py   (revision 3557)
83+++ tests/modeltests/lookup/models.py   (working copy)
84@@ -124,6 +124,22 @@
85 >>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}]
86 True
87 
88+# valuelist() returns a list of object field values instead of object instances.  Like
89+# values(), you can specify the fields whose values you want to retrieve.
90+>>> list(Article.objects.order_by('id').valuelist('id')) == [1, 2, 3, 4, 5, 6, 7]
91+True
92+
93+# if you specify more than one field, a list of tuples of field values is returned
94+>>> for t in Article.objects.order_by('id').valuelist('id', 'headline'):
95+...     t
96+(1, 'Article 1')
97+(2, 'Article 2')
98+(3, 'Article 3')
99+(4, 'Article 4')
100+(5, 'Article 5')
101+(6, 'Article 6')
102+(7, 'Article 7')
103+
104 # Every DateField and DateTimeField creates get_next_by_FOO() and
105 # get_previous_by_FOO() methods.
106 # In the case of identical date values, these methods will use the ID as a