Changeset 7462
- Timestamp:
- 04/25/08 12:08:28 (2 months ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/models/sql/query.py
r7461 r7462 747 747 748 748 def join(self, connection, always_create=False, exclusions=(), 749 promote=False, outer_if_first=False, nullable=False ):749 promote=False, outer_if_first=False, nullable=False, reuse=None): 750 750 """ 751 751 Returns an alias for the join in 'connection', either reusing an … … 757 757 lhs.lhs_col = table.col 758 758 759 If 'always_create' is True, a new alias is always created, regardless 760 of whether one already exists or not. 759 If 'always_create' is True and 'reuse' is None, a new alias is always 760 created, regardless of whether one already exists or not. Otherwise 761 'reuse' must be a set and a new join is created unless one of the 762 aliases in `reuse` can be used. 761 763 762 764 If 'exclusions' is specified, it is something satisfying the container … … 780 782 else: 781 783 lhs_table = lhs 784 785 if reuse and always_create and table in self.table_map: 786 # Convert the 'reuse' to case to be "exclude everything but the 787 # reusable set for this table". 788 exclusions = set(self.table_map[table]).difference(reuse) 789 always_create = False 782 790 t_ident = (lhs_table, table, lhs_col, col) 783 for alias in self.join_map.get(t_ident, ()): 784 if alias and not always_create and alias not in exclusions: 785 self.ref_alias(alias) 786 if promote: 787 self.promote_alias(alias) 788 return alias 791 if not always_create: 792 for alias in self.join_map.get(t_ident, ()): 793 if alias not in exclusions: 794 self.ref_alias(alias) 795 if promote: 796 self.promote_alias(alias) 797 return alias 789 798 790 799 # No reuse is possible, so we need a new alias. … … 864 873 865 874 def add_filter(self, filter_expr, connector=AND, negate=False, trim=False, 866 single_filter=False):875 can_reuse=None): 867 876 """ 868 877 Add a single filter to the query. The 'filter_expr' is a pair: … … 873 882 constructing nested queries). 874 883 875 If 'single_filter' is True, we are processing a component of a 876 multi-component filter (e.g. filter(Q1, Q2)). 884 If 'can_reuse' is a set, we are processing a component of a 885 multi-component filter (e.g. filter(Q1, Q2)). In this case, 'can_reuse' 886 will be a set of table aliases that can be reused in this filter, even 887 if we would otherwise force the creation of new aliases for a join 888 (needed for nested Q-filters). The set is updated by this method. 877 889 """ 878 890 arg, value = filter_expr … … 903 915 try: 904 916 field, target, opts, join_list, last = self.setup_joins(parts, opts, 905 alias, (connector == AND) and not single_filter, allow_many)917 alias, True, allow_many, can_reuse=can_reuse) 906 918 except MultiJoin, e: 907 919 self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level])) … … 983 995 self.where.add(entry, AND) 984 996 break 985 986 def add_q(self, q_object): 997 if can_reuse is not None: 998 can_reuse.update(join_list) 999 1000 def add_q(self, q_object, used_aliases=None): 987 1001 """ 988 1002 Adds a Q-object to the current filter. … … 1001 1015 subtree = False 1002 1016 connector = AND 1003 internal = False 1017 if used_aliases is None: 1018 used_aliases = set() 1004 1019 for child in q_object.children: 1005 1020 if isinstance(child, Node): 1006 1021 self.where.start_subtree(connector) 1007 self.add_q(child )1022 self.add_q(child, used_aliases) 1008 1023 self.where.end_subtree() 1009 1024 if q_object.negated: … … 1011 1026 else: 1012 1027 self.add_filter(child, connector, q_object.negated, 1013 single_filter=internal) 1014 internal = True 1028 can_reuse=used_aliases) 1015 1029 connector = q_object.connector 1016 1030 if subtree: … … 1018 1032 1019 1033 def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True, 1020 allow_explicit_fk=False ):1034 allow_explicit_fk=False, can_reuse=None): 1021 1035 """ 1022 1036 Compute the necessary table joins for the passage through the fields … … 1088 1102 1089 1103 int_alias = self.join((alias, table1, from_col1, to_col1), 1090 dupe_multis, joins, nullable=True )1104 dupe_multis, joins, nullable=True, reuse=can_reuse) 1091 1105 alias = self.join((int_alias, table2, from_col2, to_col2), 1092 dupe_multis, joins, nullable=True )1106 dupe_multis, joins, nullable=True, reuse=can_reuse) 1093 1107 joins.extend([int_alias, alias]) 1094 1108 elif field.rel: … … 1134 1148 1135 1149 int_alias = self.join((alias, table1, from_col1, to_col1), 1136 dupe_multis, joins, nullable=True )1150 dupe_multis, joins, nullable=True, reuse=can_reuse) 1137 1151 alias = self.join((int_alias, table2, from_col2, to_col2), 1138 dupe_multis, joins, nullable=True )1152 dupe_multis, joins, nullable=True, reuse=can_reuse) 1139 1153 joins.extend([int_alias, alias]) 1140 1154 else: … … 1154 1168 1155 1169 alias = self.join((alias, table, from_col, to_col), 1156 dupe_multis, joins, nullable=True )1170 dupe_multis, joins, nullable=True, reuse=can_reuse) 1157 1171 joins.append(alias) 1158 1172 django/branches/queryset-refactor/tests/regressiontests/queries/models.py
r7455 r7462 219 219 >>> Item.objects.filter(Q(tags=t1)).filter(Q(tags=t2)) 220 220 [<Item: one>] 221 >>> Item.objects.filter(Q(tags=t1)).filter(Q(creator__name='fred')|Q(tags=t2)) 222 [<Item: one>] 221 223 222 224 Each filter call is processed "at once" against a single table, so this is … … 224 226 things at once (rather than two tags). 225 227 >>> Item.objects.filter(Q(tags=t1) & Q(tags=t2)) 228 [] 229 >>> Item.objects.filter(Q(tags=t1), Q(creator__name='fred')|Q(tags=t2)) 226 230 [] 227 231
