Code

Ticket #3050: values.diff

File values.diff, 3.2 KB (added by Honza Král <Honza.Kral@…>, 7 years ago)

patch implementing the feature

Line 
1Index: django/db/models/query.py
2===================================================================
3--- django/db/models/query.py   (revision 4088)
4+++ django/db/models/query.py   (working copy)
5@@ -510,13 +510,22 @@
6 
7 class ValuesQuerySet(QuerySet):
8     def iterator(self):
9-        # select_related and select aren't supported in values().
10+        # select_related isn't supported in values().
11         self._select_related = False
12-        self._select = {}
13 
14         # self._fields is a list of field names to fetch.
15         if self._fields:
16-            columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
17+            if not self._select:
18+                columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields]
19+            else:
20+                columns = []
21+                for f in self._fields:
22+                    if f in [field.name for field in self.model._meta.fields]:
23+                        columns.append( self.model._meta.get_field(f, many_to_many=False).column )
24+                    elif not self._select.has_key( f ):
25+                        raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f )
26+
27+
28             field_names = self._fields
29         else: # Default to all fields.
30             columns = [f.column for f in self.model._meta.fields]
31@@ -525,6 +534,11 @@
32         cursor = connection.cursor()
33         select, sql, params = self._get_sql_clause()
34         select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
35+
36+        # Add any additional SELECTs.
37+        if self._select:
38+            select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
39+       
40         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
41         while 1:
42             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
43Index: tests/modeltests/lookup/models.py
44===================================================================
45--- tests/modeltests/lookup/models.py   (revision 4088)
46+++ tests/modeltests/lookup/models.py   (working copy)
47@@ -106,6 +106,26 @@
48 [('headline', 'Article 7'), ('id', 7)]
49 [('headline', 'Article 1'), ('id', 1)]
50 
51+# you can use values() even on extra fields
52+>>> for d in Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_one'):
53+...     i = d.items()
54+...     i.sort()
55+...     i
56+[('id', 5), ('id_plus_one', 6)]
57+[('id', 6), ('id_plus_one', 7)]
58+[('id', 4), ('id_plus_one', 5)]
59+[('id', 2), ('id_plus_one', 3)]
60+[('id', 3), ('id_plus_one', 4)]
61+[('id', 7), ('id_plus_one', 8)]
62+[('id', 1), ('id_plus_one', 2)]
63+
64+# however, an exception FieldDoesNotExist will still be thrown
65+# if you try to access non-existent field (field that is neither in model nor extra)
66+>>> Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_two')
67+Traceback (most recent call last):
68+    ...
69+FieldDoesNotExist: Article has no field named 'id_plus_two'
70+
71 # You can use values() with iterator() for memory savings, because iterator()
72 # uses database-level iteration.
73 >>> for d in Article.objects.values('id', 'headline').iterator():