Code

Ticket #7895: query_splitted_methods.diff

File query_splitted_methods.diff, 16.9 KB (added by fabiocorneti, 6 years ago)
Line 
1Index: django/db/models/sql/query.py
2===================================================================
3--- django/db/models/sql/query.py       (revision 8053)
4+++ django/db/models/sql/query.py       (working copy)
5@@ -986,6 +986,34 @@
6             self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
7                     used, next, restricted, new_nullable, dupe_set)
8 
9+    def parse_filter_expr(self, filter_expr):
10+        """
11+        Parses the given filter expression and returns a tuple containing
12+        the field names, the lookup type and the value.
13+        """
14+        arg, value = filter_expr
15+        parts = arg.split(LOOKUP_SEP)
16+        if not parts:
17+            raise FieldError("Cannot parse keyword query %r" % arg)
18+
19+        # Work out the lookup type and remove it from 'parts', if necessary.
20+        if len(parts) == 1 or parts[-1] not in self.query_terms:
21+            lookup_type = 'exact'
22+        else:
23+            lookup_type = parts.pop()
24+
25+        # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
26+        # uses of None as a query value.
27+        if value is None:
28+            if lookup_type != 'exact':
29+                raise ValueError("Cannot use None as a query value")
30+            lookup_type = 'isnull'
31+            value = True
32+        elif callable(value):
33+            value = value()
34+
35+        return (parts, lookup_type, value)
36+
37     def add_filter(self, filter_expr, connector=AND, negate=False, trim=False,
38             can_reuse=None):
39         """
40@@ -1008,37 +1036,24 @@
41         if we would otherwise force the creation of new aliases for a join
42         (needed for nested Q-filters). The set is updated by this method.
43         """
44-        arg, value = filter_expr
45-        parts = arg.split(LOOKUP_SEP)
46-        if not parts:
47-            raise FieldError("Cannot parse keyword query %r" % arg)
48+        names, lookup_type, value = self.parse_filter_expr(filter_expr)
49 
50-        # Work out the lookup type and remove it from 'parts', if necessary.
51-        if len(parts) == 1 or parts[-1] not in self.query_terms:
52-            lookup_type = 'exact'
53-        else:
54-            lookup_type = parts.pop()
55-
56-        # Interpret '__exact=None' as the sql 'is NULL'; otherwise, reject all
57-        # uses of None as a query value.
58-        if value is None:
59-            if lookup_type != 'exact':
60-                raise ValueError("Cannot use None as a query value")
61-            lookup_type = 'isnull'
62-            value = True
63-        elif callable(value):
64-            value = value()
65-
66         opts = self.get_meta()
67         alias = self.get_initial_alias()
68         allow_many = trim or not negate
69 
70         try:
71-            field, target, opts, join_list, last = self.setup_joins(parts, opts,
72+            field, target, opts, join_list, last = self.setup_joins(names, opts,
73                     alias, True, allow_many, can_reuse=can_reuse)
74         except MultiJoin, e:
75-            self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]))
76+            self.split_exclude(filter_expr, LOOKUP_SEP.join(names[:e.level]))
77             return
78+
79+        self.setup_where(field, target, join_list, last, connector, lookup_type,
80+            value, negate, trim, can_reuse)
81+
82+    def setup_where(self, field, target, join_list, last, connector,
83+        lookup_type, value, negate, trim, can_reuse):
84         final = len(join_list)
85         penultimate = last.pop()
86         if penultimate == final:
87@@ -1164,6 +1179,140 @@
88         if subtree:
89             self.where.end_subtree()
90 
91+    def setup_join(self, opts, field, model, direct, m2m, name, joins,
92+        exclusions, dupe_set, alias, dupe_multis, can_reuse=None):
93+        """
94+        Compute a single table join.
95+        """
96+        int_alias = None
97+       
98+        if model:
99+            # The field lives on a base class of the current model.
100+            for int_model in opts.get_base_chain(model):
101+                lhs_col = opts.parents[int_model].column
102+                dedupe = lhs_col in opts.duplicate_targets
103+                if dedupe:
104+                    exclusions.update(self.dupe_avoidance.get(
105+                            (id(opts), lhs_col), ()))
106+                    dupe_set.add((opts, lhs_col))
107+                opts = int_model._meta
108+                alias = self.join((alias, opts.db_table, lhs_col,
109+                        opts.pk.column), exclusions=exclusions)
110+                joins.append(alias)
111+                exclusions.add(alias)
112+                for (dupe_opts, dupe_col) in dupe_set:
113+                    self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
114+        cached_data = opts._join_cache.get(name)
115+        orig_opts = opts
116+        dupe_col = direct and field.column or field.field.column
117+        dedupe = dupe_col in opts.duplicate_targets
118+        if dupe_set or dedupe:
119+            if dedupe:
120+                dupe_set.add((opts, dupe_col))
121+            exclusions.update(self.dupe_avoidance.get((id(opts), dupe_col),
122+                    ()))
123+
124+        if direct:
125+            if m2m:
126+                # Many-to-many field defined on the current model.
127+                if cached_data:
128+                    (table1, from_col1, to_col1, table2, from_col2,
129+                            to_col2, opts, target) = cached_data
130+                else:
131+                    table1 = field.m2m_db_table()
132+                    from_col1 = opts.pk.column
133+                    to_col1 = field.m2m_column_name()
134+                    opts = field.rel.to._meta
135+                    table2 = opts.db_table
136+                    from_col2 = field.m2m_reverse_name()
137+                    to_col2 = opts.pk.column
138+                    target = opts.pk
139+                    orig_opts._join_cache[name] = (table1, from_col1,
140+                            to_col1, table2, from_col2, to_col2, opts,
141+                            target)
142+
143+                int_alias = self.join((alias, table1, from_col1, to_col1),
144+                        dupe_multis, exclusions, nullable=True,
145+                        reuse=can_reuse)
146+                alias = self.join((int_alias, table2, from_col2, to_col2),
147+                        dupe_multis, exclusions, nullable=True,
148+                        reuse=can_reuse)
149+                joins.extend([int_alias, alias])
150+            elif field.rel:
151+                # One-to-one or many-to-one field
152+                if cached_data:
153+                    (table, from_col, to_col, opts, target) = cached_data
154+                else:
155+                    opts = field.rel.to._meta
156+                    target = field.rel.get_related_field()
157+                    table = opts.db_table
158+                    from_col = field.column
159+                    to_col = target.column
160+                    orig_opts._join_cache[name] = (table, from_col, to_col,
161+                            opts, target)
162+                alias = self.join((alias, table, from_col, to_col),
163+                        exclusions=exclusions, nullable=field.null)
164+                joins.append(alias)
165+            else:
166+                # Non-relation fields.
167+                target = field
168+                return True, int_alias, alias, field, target, opts
169+        else:
170+            orig_field = field
171+            field = field.field
172+            if m2m:
173+                # Many-to-many field defined on the target model.
174+                if cached_data:
175+                    (table1, from_col1, to_col1, table2, from_col2,
176+                            to_col2, opts, target) = cached_data
177+                else:
178+                    table1 = field.m2m_db_table()
179+                    from_col1 = opts.pk.column
180+                    to_col1 = field.m2m_reverse_name()
181+                    opts = orig_field.opts
182+                    table2 = opts.db_table
183+                    from_col2 = field.m2m_column_name()
184+                    to_col2 = opts.pk.column
185+                    target = opts.pk
186+                    orig_opts._join_cache[name] = (table1, from_col1,
187+                            to_col1, table2, from_col2, to_col2, opts,
188+                            target)
189+
190+                int_alias = self.join((alias, table1, from_col1, to_col1),
191+                        dupe_multis, exclusions, nullable=True,
192+                        reuse=can_reuse)
193+                alias = self.join((int_alias, table2, from_col2, to_col2),
194+                        dupe_multis, exclusions, nullable=True,
195+                        reuse=can_reuse)
196+                joins.extend([int_alias, alias])
197+            else:
198+                # One-to-many field (ForeignKey defined on the target model)
199+                if cached_data:
200+                    (table, from_col, to_col, opts, target) = cached_data
201+                else:
202+                    local_field = opts.get_field_by_name(
203+                            field.rel.field_name)[0]
204+                    opts = orig_field.opts
205+                    table = opts.db_table
206+                    from_col = local_field.column
207+                    to_col = field.column
208+                    target = opts.pk
209+                    orig_opts._join_cache[name] = (table, from_col, to_col,
210+                            opts, target)
211+
212+                alias = self.join((alias, table, from_col, to_col),
213+                        dupe_multis, exclusions, nullable=True,
214+                        reuse=can_reuse)
215+                joins.append(alias)
216+
217+        for (dupe_opts, dupe_col) in dupe_set:
218+            if int_alias is None:
219+                self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
220+            else:
221+                self.update_dupe_avoidance(dupe_opts, dupe_col, int_alias)
222+
223+        return False, int_alias, alias, field, target, opts
224+
225     def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True,
226             allow_explicit_fk=False, can_reuse=None):
227         """
228@@ -1184,11 +1333,10 @@
229         last = [0]
230         dupe_set = set()
231         exclusions = set()
232+        int_alias = None
233         for pos, name in enumerate(names):
234-            try:
235+            if int_alias:
236                 exclusions.add(int_alias)
237-            except NameError:
238-                pass
239             exclusions.add(alias)
240             last.append(len(joins))
241             if name == 'pk':
242@@ -1213,132 +1361,13 @@
243                 for alias in joins:
244                     self.unref_alias(alias)
245                 raise MultiJoin(pos + 1)
246-            if model:
247-                # The field lives on a base class of the current model.
248-                for int_model in opts.get_base_chain(model):
249-                    lhs_col = opts.parents[int_model].column
250-                    dedupe = lhs_col in opts.duplicate_targets
251-                    if dedupe:
252-                        exclusions.update(self.dupe_avoidance.get(
253-                                (id(opts), lhs_col), ()))
254-                        dupe_set.add((opts, lhs_col))
255-                    opts = int_model._meta
256-                    alias = self.join((alias, opts.db_table, lhs_col,
257-                            opts.pk.column), exclusions=exclusions)
258-                    joins.append(alias)
259-                    exclusions.add(alias)
260-                    for (dupe_opts, dupe_col) in dupe_set:
261-                        self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
262-            cached_data = opts._join_cache.get(name)
263-            orig_opts = opts
264-            dupe_col = direct and field.column or field.field.column
265-            dedupe = dupe_col in opts.duplicate_targets
266-            if dupe_set or dedupe:
267-                if dedupe:
268-                    dupe_set.add((opts, dupe_col))
269-                exclusions.update(self.dupe_avoidance.get((id(opts), dupe_col),
270-                        ()))
271 
272-            if direct:
273-                if m2m:
274-                    # Many-to-many field defined on the current model.
275-                    if cached_data:
276-                        (table1, from_col1, to_col1, table2, from_col2,
277-                                to_col2, opts, target) = cached_data
278-                    else:
279-                        table1 = field.m2m_db_table()
280-                        from_col1 = opts.pk.column
281-                        to_col1 = field.m2m_column_name()
282-                        opts = field.rel.to._meta
283-                        table2 = opts.db_table
284-                        from_col2 = field.m2m_reverse_name()
285-                        to_col2 = opts.pk.column
286-                        target = opts.pk
287-                        orig_opts._join_cache[name] = (table1, from_col1,
288-                                to_col1, table2, from_col2, to_col2, opts,
289-                                target)
290-
291-                    int_alias = self.join((alias, table1, from_col1, to_col1),
292-                            dupe_multis, exclusions, nullable=True,
293-                            reuse=can_reuse)
294-                    alias = self.join((int_alias, table2, from_col2, to_col2),
295-                            dupe_multis, exclusions, nullable=True,
296-                            reuse=can_reuse)
297-                    joins.extend([int_alias, alias])
298-                elif field.rel:
299-                    # One-to-one or many-to-one field
300-                    if cached_data:
301-                        (table, from_col, to_col, opts, target) = cached_data
302-                    else:
303-                        opts = field.rel.to._meta
304-                        target = field.rel.get_related_field()
305-                        table = opts.db_table
306-                        from_col = field.column
307-                        to_col = target.column
308-                        orig_opts._join_cache[name] = (table, from_col, to_col,
309-                                opts, target)
310-
311-                    alias = self.join((alias, table, from_col, to_col),
312-                            exclusions=exclusions, nullable=field.null)
313-                    joins.append(alias)
314-                else:
315-                    # Non-relation fields.
316-                    target = field
317-                    break
318-            else:
319-                orig_field = field
320-                field = field.field
321-                if m2m:
322-                    # Many-to-many field defined on the target model.
323-                    if cached_data:
324-                        (table1, from_col1, to_col1, table2, from_col2,
325-                                to_col2, opts, target) = cached_data
326-                    else:
327-                        table1 = field.m2m_db_table()
328-                        from_col1 = opts.pk.column
329-                        to_col1 = field.m2m_reverse_name()
330-                        opts = orig_field.opts
331-                        table2 = opts.db_table
332-                        from_col2 = field.m2m_column_name()
333-                        to_col2 = opts.pk.column
334-                        target = opts.pk
335-                        orig_opts._join_cache[name] = (table1, from_col1,
336-                                to_col1, table2, from_col2, to_col2, opts,
337-                                target)
338-
339-                    int_alias = self.join((alias, table1, from_col1, to_col1),
340-                            dupe_multis, exclusions, nullable=True,
341-                            reuse=can_reuse)
342-                    alias = self.join((int_alias, table2, from_col2, to_col2),
343-                            dupe_multis, exclusions, nullable=True,
344-                            reuse=can_reuse)
345-                    joins.extend([int_alias, alias])
346-                else:
347-                    # One-to-many field (ForeignKey defined on the target model)
348-                    if cached_data:
349-                        (table, from_col, to_col, opts, target) = cached_data
350-                    else:
351-                        local_field = opts.get_field_by_name(
352-                                field.rel.field_name)[0]
353-                        opts = orig_field.opts
354-                        table = opts.db_table
355-                        from_col = local_field.column
356-                        to_col = field.column
357-                        target = opts.pk
358-                        orig_opts._join_cache[name] = (table, from_col, to_col,
359-                                opts, target)
360-
361-                    alias = self.join((alias, table, from_col, to_col),
362-                            dupe_multis, exclusions, nullable=True,
363-                            reuse=can_reuse)
364-                    joins.append(alias)
365-
366-            for (dupe_opts, dupe_col) in dupe_set:
367-                try:
368-                    self.update_dupe_avoidance(dupe_opts, dupe_col, int_alias)
369-                except NameError:
370-                    self.update_dupe_avoidance(dupe_opts, dupe_col, alias)
371-
372+            final, int_alias, alias, field, target, opts = self.setup_join(opts,
373+                field, model, direct, m2m, name, joins, exclusions,
374+                dupe_set, alias, dupe_multis, can_reuse=can_reuse)
375+            if final:
376+                break
377+       
378         if pos != len(names) - 1:
379             raise FieldError("Join on field %r not permitted." % name)
380