mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #28454 -- Simplifed use of Query.setup_joins() by returning a named tuple.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							8df7681d0e
						
					
				
				
					commit
					32d1bf2bdb
				
			| @@ -818,8 +818,8 @@ class SQLCompiler: | ||||
|                 related_field_name = f.related_query_name() | ||||
|                 fields_found.add(related_field_name) | ||||
|  | ||||
|                 _, _, _, joins, _ = self.query.setup_joins([related_field_name], opts, root_alias) | ||||
|                 alias = joins[-1] | ||||
|                 join_info = self.query.setup_joins([related_field_name], opts, root_alias) | ||||
|                 alias = join_info.joins[-1] | ||||
|                 from_parent = issubclass(model, opts.model) and model is not opts.model | ||||
|                 klass_info = { | ||||
|                     'model': model, | ||||
|   | ||||
| @@ -6,7 +6,7 @@ themselves do not have to (and could be backed by things other than SQL | ||||
| databases). The abstraction barrier only works one way: this module has to know | ||||
| all about the internals of models in order to get the information it needs. | ||||
| """ | ||||
| from collections import Counter, Iterator, Mapping, OrderedDict | ||||
| from collections import Counter, Iterator, Mapping, OrderedDict, namedtuple | ||||
| from contextlib import suppress | ||||
| from itertools import chain, count, product | ||||
| from string import ascii_uppercase | ||||
| @@ -44,6 +44,12 @@ def get_field_names_from_opts(opts): | ||||
|     )) | ||||
|  | ||||
|  | ||||
| JoinInfo = namedtuple( | ||||
|     'JoinInfo', | ||||
|     ('final_field', 'targets', 'opts', 'joins', 'path') | ||||
| ) | ||||
|  | ||||
|  | ||||
| class RawQuery: | ||||
|     """A single raw SQL query.""" | ||||
|  | ||||
| @@ -935,10 +941,9 @@ class Query: | ||||
|                 curr_opts = int_model._meta | ||||
|                 continue | ||||
|             link_field = curr_opts.get_ancestor_link(int_model) | ||||
|             _, _, _, joins, _ = self.setup_joins( | ||||
|                 [link_field.name], curr_opts, alias) | ||||
|             join_info = self.setup_joins([link_field.name], curr_opts, alias) | ||||
|             curr_opts = int_model._meta | ||||
|             alias = seen[int_model] = joins[-1] | ||||
|             alias = seen[int_model] = join_info.joins[-1] | ||||
|         return alias or seen[None] | ||||
|  | ||||
|     def add_annotation(self, annotation, alias, is_summary=False): | ||||
| @@ -1146,39 +1151,38 @@ class Query: | ||||
|         allow_many = not branch_negated or not split_subq | ||||
|  | ||||
|         try: | ||||
|             field, sources, opts, join_list, path = self.setup_joins( | ||||
|                 parts, opts, alias, can_reuse=can_reuse, allow_many=allow_many) | ||||
|             join_info = self.setup_joins(parts, opts, alias, can_reuse=can_reuse, allow_many=allow_many) | ||||
|  | ||||
|             # Prevent iterator from being consumed by check_related_objects() | ||||
|             if isinstance(value, Iterator): | ||||
|                 value = list(value) | ||||
|             self.check_related_objects(field, value, opts) | ||||
|             self.check_related_objects(join_info.final_field, value, join_info.opts) | ||||
|  | ||||
|             # split_exclude() needs to know which joins were generated for the | ||||
|             # lookup parts | ||||
|             self._lookup_joins = join_list | ||||
|             self._lookup_joins = join_info.joins | ||||
|         except MultiJoin as e: | ||||
|             return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]), | ||||
|                                       can_reuse, e.names_with_path) | ||||
|  | ||||
|         # Update used_joins before trimming since they are reused to determine | ||||
|         # which joins could be later promoted to INNER. | ||||
|         used_joins.update(join_list) | ||||
|         targets, alias, join_list = self.trim_joins(sources, join_list, path) | ||||
|         used_joins.update(join_info.joins) | ||||
|         targets, alias, join_list = self.trim_joins(join_info.targets, join_info.joins, join_info.path) | ||||
|         if can_reuse is not None: | ||||
|             can_reuse.update(join_list) | ||||
|  | ||||
|         if field.is_relation: | ||||
|         if join_info.final_field.is_relation: | ||||
|             # No support for transforms for relational fields | ||||
|             num_lookups = len(lookups) | ||||
|             if num_lookups > 1: | ||||
|                 raise FieldError('Related Field got invalid lookup: {}'.format(lookups[0])) | ||||
|             if len(targets) == 1: | ||||
|                 col = targets[0].get_col(alias, field) | ||||
|                 col = targets[0].get_col(alias, join_info.final_field) | ||||
|             else: | ||||
|                 col = MultiColSource(alias, targets, sources, field) | ||||
|                 col = MultiColSource(alias, targets, join_info.targets, join_info.final_field) | ||||
|         else: | ||||
|             col = targets[0].get_col(alias, field) | ||||
|             col = targets[0].get_col(alias, join_info.final_field) | ||||
|  | ||||
|         condition = self.build_lookup(lookups, col, value) | ||||
|         lookup_type = condition.lookup_name | ||||
| @@ -1200,7 +1204,7 @@ class Query: | ||||
|                 #   <=> | ||||
|                 # NOT (col IS NOT NULL AND col = someval). | ||||
|                 lookup_class = targets[0].get_lookup('isnull') | ||||
|                 clause.add(lookup_class(targets[0].get_col(alias, sources[0]), False), AND) | ||||
|                 clause.add(lookup_class(targets[0].get_col(alias, join_info.targets[0]), False), AND) | ||||
|         return clause, used_joins if not require_outer else () | ||||
|  | ||||
|     def add_filter(self, filter_clause): | ||||
| @@ -1383,7 +1387,7 @@ class Query: | ||||
|             reuse = can_reuse if join.m2m else None | ||||
|             alias = self.join(connection, reuse=reuse) | ||||
|             joins.append(alias) | ||||
|         return final_field, targets, opts, joins, path | ||||
|         return JoinInfo(final_field, targets, opts, joins, path) | ||||
|  | ||||
|     def trim_joins(self, targets, joins, path): | ||||
|         """ | ||||
| @@ -1425,16 +1429,14 @@ class Query: | ||||
|                 return self.annotation_select[name] | ||||
|         else: | ||||
|             field_list = name.split(LOOKUP_SEP) | ||||
|             field, sources, opts, join_list, path = self.setup_joins( | ||||
|                 field_list, self.get_meta(), | ||||
|                 self.get_initial_alias(), reuse) | ||||
|             targets, _, join_list = self.trim_joins(sources, join_list, path) | ||||
|             join_info = self.setup_joins(field_list, self.get_meta(), self.get_initial_alias(), reuse) | ||||
|             targets, _, join_list = self.trim_joins(join_info.targets, join_info.joins, join_info.path) | ||||
|             if len(targets) > 1: | ||||
|                 raise FieldError("Referencing multicolumn fields with F() objects " | ||||
|                                  "isn't supported") | ||||
|             if reuse is not None: | ||||
|                 reuse.update(join_list) | ||||
|             col = targets[0].get_col(join_list[-1], sources[0]) | ||||
|             col = targets[0].get_col(join_list[-1], join_info.targets[0]) | ||||
|             return col | ||||
|  | ||||
|     def split_exclude(self, filter_expr, prefix, can_reuse, names_with_path): | ||||
| @@ -1586,9 +1588,12 @@ class Query: | ||||
|             for name in field_names: | ||||
|                 # Join promotion note - we must not remove any rows here, so | ||||
|                 # if there is no existing joins, use outer join. | ||||
|                 _, targets, _, joins, path = self.setup_joins( | ||||
|                     name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m) | ||||
|                 targets, final_alias, joins = self.trim_joins(targets, joins, path) | ||||
|                 join_info = self.setup_joins(name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m) | ||||
|                 targets, final_alias, joins = self.trim_joins( | ||||
|                     join_info.targets, | ||||
|                     join_info.joins, | ||||
|                     join_info.path, | ||||
|                 ) | ||||
|                 for target in targets: | ||||
|                     cols.append(target.get_col(final_alias)) | ||||
|             if cols: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user