Changeset 7255
- Timestamp:
- 03/17/08 08:32:11 (8 months ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/models/fields/related.py
r7244 r7255 29 29 Adds a lookup on ``cls`` when a related field is defined using a string, 30 30 i.e.:: 31 31 32 32 class MyModel(Model): 33 33 fk = ForeignKey("AnotherModel") 34 34 35 35 This string can be: 36 36 37 37 * RECURSIVE_RELATIONSHIP_CONSTANT (i.e. "self") to indicate a recursive 38 38 relation. 39 39 40 40 * The name of a model (i.e "AnotherModel") to indicate another model in 41 41 the same app. 42 42 43 43 * An app-label and model name (i.e. "someapp.AnotherModel") to indicate 44 44 another model in a different app. 45 45 46 46 If the other model hasn't yet been loaded -- almost a given if you're using 47 47 lazy relationships -- then the relation won't be set up until the … … 52 52 app_label = cls._meta.app_label 53 53 model_name = cls.__name__ 54 54 55 55 else: 56 56 # Look for an "app.Model" relation … … 61 61 app_label = cls._meta.app_label 62 62 model_name = relation 63 63 64 64 # Try to look up the related model, and if it's already loaded resolve the 65 65 # string right away. If get_model returns None, it means that the related 66 # model isn't loaded yet, so we need to pend the relation until the class 66 # model isn't loaded yet, so we need to pend the relation until the class 67 67 # is prepared. 68 68 model = get_model(app_label, model_name, False) … … 74 74 value = (cls, field) 75 75 pending_lookups.setdefault(key, []).append(value) 76 76 77 77 def do_pending_lookups(sender): 78 78 """ … … 531 531 tied. 532 532 """ 533 return self.to._meta.get_field_by_name(self.field_name, True)[0] 533 data = self.to._meta.get_field_by_name(self.field_name) 534 if not data[2]: 535 raise FieldDoesNotExist("No related field named '%s'" % 536 self.field_name) 537 return data[0] 534 538 535 539 class OneToOneRel(ManyToOneRel): django/branches/queryset-refactor/django/db/models/options.py
r7253 r7255 232 232 raise FieldDoesNotExist, '%s has no field named %r' % (self.object_name, name) 233 233 234 def get_field_by_name(self, name , only_direct=False):234 def get_field_by_name(self, name): 235 235 """ 236 236 Returns the (field_object, model, direct, m2m), where field_object is … … 242 242 with it). 243 243 244 If 'only_direct' is True, only forwards relations (and non-relations)245 are considered in the result.246 247 244 Uses a cache internally, so after the first access, this is very fast. 248 245 """ 249 246 try: 250 result = self._name_map.get(name)251 except AttributeError:252 cache = self.init_name_map()253 result = cache.get(name)254 255 if not result or (only_direct and not result[2]):247 try: 248 return self._name_map[name] 249 except AttributeError: 250 cache = self.init_name_map() 251 return self._name_map[name] 252 except KeyError: 256 253 raise FieldDoesNotExist('%s has no field named %r' 257 254 % (self.object_name, name)) 258 return result259 255 260 256 def get_all_field_names(self): django/branches/queryset-refactor/django/db/models/sql/query.py
r7253 r7255 47 47 self.alias_map = {} # Maps alias to join information 48 48 self.table_map = {} # Maps table names to list of aliases. 49 self.rev_join_map = {} # Reverse of join_map. (FIXME: Update comment) 49 self.join_map = {} 50 self.rev_join_map = {} # Reverse of join_map. 50 51 self.quote_cache = {} 51 52 self.default_cols = True … … 131 132 obj.alias_map = self.alias_map.copy() 132 133 obj.table_map = self.table_map.copy() 134 obj.join_map = self.join_map.copy() 133 135 obj.rev_join_map = self.rev_join_map.copy() 134 136 obj.quote_cache = {} … … 272 274 # Work out how to relabel the rhs aliases, if necessary. 273 275 change_map = {} 274 used = {}276 used = set() 275 277 conjunction = (connector == AND) 276 278 first = True … … 282 284 new_alias = self.join(rhs.rev_join_map[alias], 283 285 (conjunction and not first), used, promote, not conjunction) 284 used [new_alias] = None286 used.add(new_alias) 285 287 change_map[alias] = new_alias 286 288 first = False … … 412 414 result = [] 413 415 qn = self.quote_name_unless_alias 416 qn2 = self.connection.ops.quote_name 414 417 first = True 415 418 for alias in self.tables: 416 419 if not self.alias_refcount[alias]: 417 420 continue 418 join = self.alias_map[alias] 419 if join: 420 name, alias, join_type, lhs, lhs_col, col, nullable = join 421 alias_str = (alias != name and ' AS %s' % alias or '') 422 else: 423 join_type = None 424 alias_str = '' 425 name = alias 421 name, alias, join_type, lhs, lhs_col, col, nullable = self.alias_map[alias] 422 alias_str = (alias != name and ' AS %s' % alias or '') 426 423 if join_type and not first: 427 424 result.append('%s %s%s ON (%s.%s = %s.%s)' 428 425 % (join_type, qn(name), alias_str, qn(lhs), 429 qn (lhs_col), qn(alias), qn(col)))426 qn2(lhs_col), qn(alias), qn2(col))) 430 427 else: 431 428 connector = not first and ', ' or '' … … 473 470 ordering = self.order_by or self.model._meta.ordering 474 471 qn = self.quote_name_unless_alias 472 qn2 = self.connection.ops.quote_name 475 473 distinct = self.distinct 476 474 select_aliases = self._select_aliases … … 493 491 continue 494 492 if '.' in field: 495 # This came in through an extra(ordering=...) addition. Pass it 496 # on verbatim, after mapping the table name to an alias, if 497 # necessary. 493 # This came in through an extra(order_by=...) addition. Pass it 494 # on verbatim. 498 495 col, order = get_order_dir(field, asc) 499 496 table, col = col.split('.', 1) 500 elt = '%s.%s' % (qn( self.table_alias(table)[0]), col)497 elt = '%s.%s' % (qn(table), col) 501 498 if not distinct or elt in select_aliases: 502 499 result.append('%s %s' % (elt, order)) … … 506 503 for table, col, order in self.find_ordering_name(field, 507 504 self.model._meta, default_order=asc): 508 elt = '%s.%s' % (qn(table), qn (col))505 elt = '%s.%s' % (qn(table), qn2(col)) 509 506 if not distinct or elt in select_aliases: 510 507 result.append('%s %s' % (elt, order)) … … 528 525 alias = self.get_initial_alias() 529 526 try: 530 field, target, opts, joins = self.setup_joins(pieces, opts, alias,531 False, False)527 field, target, opts, joins, last = self.setup_joins(pieces, opts, 528 alias, False, False) 532 529 except JoinError: 533 530 raise FieldError("Cannot order by many-valued field: '%s'" % name) 534 alias = joins[-1] [-1]531 alias = joins[-1] 535 532 col = target.column 536 533 … … 541 538 if not already_seen: 542 539 already_seen = set() 543 join_tuple = tuple( [tuple(j) for j in joins])540 join_tuple = tuple(joins) 544 541 if join_tuple in already_seen: 545 542 raise FieldError('Infinite loop caused by ordering.') … … 571 568 most recently created alias for the table (if one exists) is reused. 572 569 """ 573 if not create and table_name in self.table_map: 574 alias = self.table_map[table_name][-1] 570 current = self.table_map.get(table_name) 571 if not create and current: 572 alias = current[0] 575 573 self.alias_refcount[alias] += 1 576 574 return alias, False 577 575 578 576 # Create a new alias for this table. 579 if table_name not in self.table_map: 577 if current: 578 alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1) 579 current.append(alias) 580 else: 580 581 # The first occurence of a table uses the table name directly. 581 582 alias = table_name 582 else: 583 alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1) 583 self.table_map[alias] = [alias] 584 584 self.alias_refcount[alias] = 1 585 self.alias_map[alias] = None 586 self.table_map.setdefault(table_name, []).append(alias) 585 #self.alias_map[alias] = None 587 586 self.tables.append(alias) 588 587 return alias, True … … 630 629 alias_data[RHS_ALIAS] = new_alias 631 630 632 self.rev_join_map[new_alias] = self.rev_join_map[old_alias] 631 t = self.rev_join_map[old_alias] 632 self.join_map[t] = new_alias 633 self.rev_join_map[new_alias] = t 633 634 del self.rev_join_map[old_alias] 634 635 self.alias_refcount[new_alias] = self.alias_refcount[old_alias] … … 655 656 self.alias_map[alias] = tuple(data) 656 657 657 658 658 def bump_prefix(self): 659 659 """ … … 725 725 """ 726 726 lhs, table, lhs_col, col = connection 727 if lhs is None: 728 lhs_table = None 729 is_table = False 730 elif lhs not in self.alias_map: 727 if lhs in self.alias_map: 728 lhs_table = self.alias_map[lhs][TABLE_NAME] 729 else: 731 730 lhs_table = lhs 732 is_table = True733 else:734 lhs_table = self.alias_map[lhs][TABLE_NAME]735 is_table = False736 731 t_ident = (lhs_table, table, lhs_col, col) 737 if not always_create: 738 for alias, row in self.rev_join_map.items(): 739 if t_ident == row and alias not in exclusions: 740 self.ref_alias(alias) 741 if promote: 742 self.promote_alias(alias) 743 return alias 744 # If we get to here (no non-excluded alias exists), we'll fall 745 # through to creating a new alias. 732 alias = self.join_map.get(t_ident) 733 if alias and not always_create and alias not in exclusions: 734 self.ref_alias(alias) 735 if promote: 736 self.promote_alias(alias) 737 return alias 746 738 747 739 # No reuse is possible, so we need a new alias. 748 assert not is_table, \749 "Must pass in lhs alias when creating a new join."750 740 alias, _ = self.table_alias(table, True) 751 741 if not lhs: … … 759 749 join = (table, alias, join_type, lhs, lhs_col, col, nullable) 760 750 self.alias_map[alias] = join 751 self.join_map[t_ident] = alias 761 752 self.rev_join_map[alias] = t_ident 762 753 return alias … … 856 847 857 848 try: 858 field, target, opts, join_list = self.setup_joins(parts, opts,849 field, target, opts, join_list, last = self.setup_joins(parts, opts, 859 850 alias, (connector == AND), allow_many) 860 851 except JoinError, e: 861 852 self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level])) 862 853 return 854 final = len(join_list) 855 penultimate = last.pop() 856 if penultimate == final: 857 penultimate = last.pop() 863 858 if trim and len(join_list) > 1: 864 extra = join_list[-1] 865 join_list = join_list[:-1] 859 extra = join_list[penultimate:] 860 join_list = join_list[:penultimate] 861 final = penultimate 862 penultimate = last.pop() 866 863 col = self.alias_map[extra[0]][LHS_JOIN_COL] 867 864 for alias in extra: … … 869 866 else: 870 867 col = target.column 871 alias = join_list[-1] [-1]872 873 if join_list:868 alias = join_list[-1] 869 870 if final > 1: 874 871 # An optimization: if the final join is against the same column as 875 872 # we are comparing against, we can go back one step in the join … … 881 878 alias = join[LHS_ALIAS] 882 879 col = join[LHS_JOIN_COL] 883 if len(join_list[-1]) == 1:884 join_list = join_list[:-1]885 else:886 join_list[-1] = join_list[-1][:-1]880 join_list = join_list[:-1] 881 final -= 1 882 if final == penultimate: 883 penultimate = last.pop() 887 884 888 885 if (lookup_type == 'isnull' and value is True and not negate and 889 (len(join_list) > 1 or len(join_list[0]) > 1)):886 final > 1): 890 887 # If the comparison is against NULL, we need to use a left outer 891 888 # join when connecting to the previous model. We make that 892 889 # adjustment here. We don't do this unless needed as it's less 893 890 # efficient at the database level. 894 self.promote_alias(join_list[ -1][0])891 self.promote_alias(join_list[penultimate]) 895 892 896 893 if connector == OR: … … 900 897 # make the new additions (and any existing ones not used in the new 901 898 # join list) an outer join. 902 join_it = iter tools.chain(*join_list)899 join_it = iter(join_list) 903 900 table_it = iter(self.tables) 904 901 join_it.next(), table_it.next() … … 932 929 933 930 if negate: 934 count = 0 935 for join in join_list: 936 count += len(join) 937 for alias in join: 938 self.promote_alias(alias) 931 for alias in join_list: 932 self.promote_alias(alias) 939 933 if not merged: 940 934 self.where.negate() 941 if count> 1 and lookup_type != 'isnull':935 if final > 1 and lookup_type != 'isnull': 942 936 j_col = self.alias_map[alias][RHS_JOIN_COL] 943 937 entry = Node([(alias, j_col, None, 'isnull', True)]) … … 990 984 list of tables joined. 991 985 """ 992 joins = [ [alias]]993 used = set()986 joins = [alias] 987 last = [0] 994 988 for pos, name in enumerate(names): 995 used.update(joins[-1])989 last.append(len(joins)) 996 990 if name == 'pk': 997 991 name = opts.pk.name … … 1012 1006 "Choices are: %s" % (name, ", ".join(names))) 1013 1007 if not allow_many and (m2m or not direct): 1014 for join in joins: 1015 for alias in join: 1016 self.unref_alias(alias) 1008 for alias in joins: 1009 self.unref_alias(alias) 1017 1010 raise JoinError(pos + 1) 1018 1011 if model: … … 1023 1016 opts = int_model._meta 1024 1017 alias = self.join((alias, opts.db_table, lhs_col, 1025 opts.pk.column), exclusions=used) 1026 alias_list.append(alias) 1027 joins.append(alias_list) 1018 opts.pk.column), exclusions=joins) 1019 joins.append(alias) 1028 1020 cached_data = opts._join_cache.get(name) 1029 1021 orig_opts = opts … … 1049 1041 1050 1042 int_alias = self.join((alias, table1, from_col1, to_col1), 1051 dupe_multis, used, nullable=True)1043 dupe_multis, joins, nullable=True) 1052 1044 alias = self.join((int_alias, table2, from_col2, to_col2), 1053 dupe_multis, used, nullable=True)1054 joins. append([int_alias, alias])1045 dupe_multis, joins, nullable=True) 1046 joins.extend([int_alias, alias]) 1055 1047 elif field.rel: 1056 1048 # One-to-one or many-to-one field … … 1067 1059 1068 1060 alias = self.join((alias, table, from_col, to_col), 1069 exclusions= used, nullable=field.null)1070 joins.append( [alias])1061 exclusions=joins, nullable=field.null) 1062 joins.append(alias) 1071 1063 else: 1072 1064 # Non-relation fields. … … 1095 1087 1096 1088 int_alias = self.join((alias, table1, from_col1, to_col1), 1097 dupe_multis, used, nullable=True)1089 dupe_multis, joins, nullable=True) 1098 1090 alias = self.join((int_alias, table2, from_col2, to_col2), 1099 dupe_multis, used, nullable=True)1100 joins. append([int_alias, alias])1091 dupe_multis, joins, nullable=True) 1092 joins.extend([int_alias, alias]) 1101 1093 else: 1102 1094 # One-to-many field (ForeignKey defined on the target model) … … 1115 1107 1116 1108 alias = self.join((alias, table, from_col, to_col), 1117 dupe_multis, used, nullable=True)1118 joins.append( [alias])1109 dupe_multis, joins, nullable=True) 1110 joins.append(alias) 1119 1111 1120 1112 if pos != len(names) - 1: 1121 1113 raise FieldError("Join on field %r not permitted." % name) 1122 1114 1123 return field, target, opts, joins 1115 return field, target, opts, joins, last 1124 1116 1125 1117 def split_exclude(self, filter_expr, prefix): … … 1180 1172 try: 1181 1173 for name in field_names: 1182 u1, target, u2, joins = self.setup_joins(name.split(LOOKUP_SEP), 1183 opts, alias, False, allow_m2m, True) 1184 self.select.append((joins[-1][-1], target.column)) 1174 u1, target, u2, joins, u3 = self.setup_joins( 1175 name.split(LOOKUP_SEP), opts, alias, False, allow_m2m, 1176 True) 1177 self.select.append((joins[-1], target.column)) 1185 1178 except JoinError: 1186 1179 raise FieldError("Invalid field name: '%s'" % name) … … 1295 1288 opts = self.model._meta 1296 1289 alias = self.get_initial_alias() 1297 field, col, opts, joins = self.setup_joins(start.split(LOOKUP_SEP),1298 opts, alias, False)1299 alias = joins[ -1][0]1290 field, col, opts, joins, last = self.setup_joins( 1291 start.split(LOOKUP_SEP), opts, alias, False) 1292 alias = joins[last[-1]] 1300 1293 self.select = [(alias, self.alias_map[alias][RHS_JOIN_COL])] 1301 1294 self.start_meta = opts … … 1304 1297 # joins. So we need to unref everything once, and everything prior to 1305 1298 # the final join a second time. 1306 for join in joins[:-1]: 1307 for alias in join: 1308 self.unref_alias(alias) 1309 self.unref_alias(alias) 1310 for alias in joins[-1]: 1299 for alias in joins: 1300 self.unref_alias(alias) 1301 for alias in joins[:last[-1]]: 1311 1302 self.unref_alias(alias) 1312 1303
