Code

Ticket #4997: query.py.diff

File query.py.diff, 4.2 KB (added by David Cramer <dcramer@…>, 7 years ago)
Line 
1--- /django/trunk/django/db/models/query.py     2007-07-27 19:38:58.000000000 -0700
2+++ /django/trunk/django/db/models/query.py     2007-07-27 19:38:43.000000000 -0700
3@@ -88,6 +88,7 @@
4         self.model = model
5         self._filters = Q()
6         self._order_by = None        # Ordering, e.g. ('date', '-name'). If None, use model's ordering.
7+        self._group_by = []        # Grouping, e.g. ('foreignkey').
8         self._select_related = False # Whether to fill cache for related objects.
9         self._max_related_depth = 0  # Maximum "depth" for select_related
10         self._distinct = False       # Whether the query should use SELECT DISTINCT.
11@@ -215,6 +216,8 @@
12         If the queryset is already cached (i.e. self._result_cache is set) this
13         simply returns the length of the cached results set to avoid multiple
14         SELECT COUNT(*) calls.
15+
16+        If they were using GROUP_BY we have to change this to a COUNT(DISTINCT).
17         """
18         if self._result_cache is not None:
19             return len(self._result_cache)
20@@ -228,6 +231,14 @@
21         counter._offset = None
22         counter._limit = None
23 
24+        if counter._group_by:
25+            id_col = ", ".join(counter.groupby2columns(self.model._meta))
26+            counter._group_by = ()
27+            counter._distinct = True
28+        elif self._distinct:
29+            id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
30+                backend.quote_name(self.model._meta.pk.column))
31+
32         try:
33             select, sql, params = counter._get_sql_clause()
34         except EmptyResultSet:
35@@ -418,6 +429,10 @@
36                 "Cannot reorder a query once a slice has been taken."
37         return self._clone(_order_by=field_names)
38 
39+    def group_by(self, *field_names):
40+        "Returns a new QuerySet instance with '_group_by' modified."
41+        return self._clone(_group_by=field_names)
42+
43     def distinct(self, true_or_false=True):
44         "Returns a new QuerySet instance with '_distinct' modified."
45         return self._clone(_distinct=true_or_false)
46@@ -443,6 +458,7 @@
47         c.model = self.model
48         c._filters = self._filters
49         c._order_by = self._order_by
50+        c._group_by = self._group_by
51         c._select_related = self._select_related
52         c._max_related_depth = self._max_related_depth
53         c._distinct = self._distinct
54@@ -473,6 +489,9 @@
55         if (self._order_by is not None and len(self._order_by) > 0) and \
56            (combined._order_by is None or len(combined._order_by) == 0):
57             combined._order_by = self._order_by
58+        if (self._group_by is not None and len(self._group_by) > 0) and \
59+           (combined._group_by is None or len(combined._group_by) == 0):
60+            combined._group_by = self._group_by
61         return combined
62 
63     def _get_data(self):
64@@ -523,6 +542,11 @@
65         if where:
66             sql.append(where and "WHERE " + " AND ".join(where))
67 
68+        # GROUP BY clause
69+        group_by = self.groupby2columns(opts)
70+        if group_by:
71+            sql.append("GROUP BY " + ", ".join(group_by))
72+
73         # ORDER BY clause
74         order_by = []
75         if self._order_by is not None:
76@@ -561,6 +585,22 @@
77 
78         return select, " ".join(sql), params
79 
80+    def groupby2columns(self, opts):
81+        group_by = []
82+        for col_name in self._group_by:
83+            if "." in col_name:
84+                table_prefix, col_name = col_name.split('.', 1)
85+                table_prefix = backend.quote_name(table_prefix) + '.'
86+            else:
87+                # Use the database table as a column prefix if it wasn't given,
88+                # and if the requested column isn't a custom SELECT.
89+                if "." not in col_name and col_name not in (self._select or ()):
90+                    table_prefix = backend.quote_name(opts.db_table) + '.'
91+                else:
92+                    table_prefix = ''
93+            group_by.append('%s%s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts))))
94+        return group_by
95+
96 # Use the backend's QuerySet class if it defines one, otherwise use _QuerySet.
97 if hasattr(backend, 'get_query_set_class'):
98     QuerySet = backend.get_query_set_class(_QuerySet)