Changeset 6121
- Timestamp:
- 09/13/07 01:33:41 (1 year ago)
- Files:
-
- django/branches/queryset-refactor/django/db/models/base.py (modified) (1 diff)
- django/branches/queryset-refactor/django/db/models/sql/query.py (modified) (9 diffs)
- django/branches/queryset-refactor/django/db/models/sql/where.py (modified) (1 diff)
- django/branches/queryset-refactor/django/utils/tree.py (modified) (2 diffs)
- django/branches/queryset-refactor/tests/modeltests/lookup/models.py (modified) (1 diff)
- django/branches/queryset-refactor/tests/regressiontests/queries/models.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/models/base.py
r5975 r6121 335 335 param = smart_str(getattr(self, field.attname)) 336 336 q = self.__class__._default_manager.filter(**kwargs).order_by((not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name) 337 q. _where.append(where)338 q._params.extend([param, param,getattr(self, self._meta.pk.attname)])337 q.extra(where=where, params=[param, param, 338 getattr(self, self._meta.pk.attname)]) 339 339 try: 340 340 return q[0] django/branches/queryset-refactor/django/db/models/sql/query.py
r6120 r6121 11 11 12 12 from django.utils import tree 13 from django.db.models.sql.where import WhereNode, AND 13 from django.db.models.sql.where import WhereNode, AND, OR 14 14 from django.db.models.sql.datastructures import Count 15 15 from django.db.models.fields import FieldDoesNotExist … … 234 234 change_map = {} 235 235 used = {} 236 first_new_join = True 236 237 for alias in rhs.tables: 238 if not rhs.alias_map[alias][ALIAS_REFCOUNT]: 239 # An unused alias. 240 continue 237 241 promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == 238 242 self.LOUTER) 239 243 new_alias = self.join(rhs.rev_join_map[alias], exclusions=used, 240 promote=promote) 244 promote=promote, outer_if_first=True) 245 if self.alias_map[alias][ALIAS_REFCOUNT] == 1: 246 first_new_join = False 241 247 used[new_alias] = None 242 248 change_map[alias] = new_alias 249 250 # So that we don't exclude valid results, the first join that is 251 # exclusive to the lhs (self) must be converted to an outer join. 252 for alias in self.tables[1:]: 253 if self.alias_map[alias][ALIAS_REFCOUNT] == 1: 254 self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 255 break 243 256 244 257 # Now relabel a copy of the rhs where-clause and add it to the current … … 381 394 self.alias_map[alias][ALIAS_REFCOUNT] -= 1 382 395 396 def promote_alias(self, alias): 397 """ Promotes the join type of an alias to an outer join. """ 398 self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 399 383 400 def join(self, (lhs, table, lhs_col, col), always_create=False, 384 exclusions=(), promote=False ):401 exclusions=(), promote=False, outer_if_first=False): 385 402 """ 386 403 Returns an alias for a join between 'table' and 'lhs' on the given … … 399 416 the alias previously existed, the join type will be promoted from INNER 400 417 to LOUTER, if necessary). 418 419 If 'outer_if_first' is True and a new join is created, it will have the 420 LOUTER join type. This is used when joining certain types of querysets 421 and Q-objects together. 401 422 """ 402 423 if lhs not in self.alias_map: … … 423 444 "Must pass in lhs alias when creating a new join." 424 445 alias, _ = self.table_alias(table, True) 425 join_type = promoteand self.LOUTER or self.INNER446 join_type = (promote or outer_if_first) and self.LOUTER or self.INNER 426 447 join = [table, alias, join_type, lhs, lhs_col, col] 427 448 if not lhs: … … 488 509 alias = self.join((None, opts.db_table, None, None)) 489 510 dupe_multis = (connection == AND) 490 last = None 511 seen_aliases = [] 512 done_split = not self.where 491 513 492 514 # FIXME: Using enumerate() here is expensive. We only need 'i' to … … 499 521 name = target_field.name 500 522 if joins is not None: 523 seen_aliases.extend(joins) 501 524 last = joins 502 525 alias = joins[-1] 526 if connection == OR and not done_split: 527 if self.alias_map[joins[0]][ALIAS_REFCOUNT] == 1: 528 done_split = True 529 self.promote_alias(joins[0]) 530 for t in self.tables[1:]: 531 if t in seen_aliases: 532 continue 533 self.promote_alias(t) 534 break 535 else: 536 seen_aliases.extend(joins) 503 537 else: 504 538 # Normal field lookup must be the last field in the filter. 505 539 if i != len(parts) - 1: 506 raise TypeError("Join son field %r not permitted."540 raise TypeError("Join on field %r not permitted." 507 541 % name) 508 542 509 543 col = target_col or target_field.column 510 544 511 if target_field is opts.pk and last:545 if target_field is opts.pk and seen_aliases: 512 546 # An optimization: if the final join is against a primary key, 513 547 # we can go back one step in the join chain and compare against … … 515 549 # one less table join. 516 550 self.unref_alias(alias) 517 join = self.alias_map[ last[-1]][ALIAS_JOIN]551 join = self.alias_map[seen_aliases[-1]][ALIAS_JOIN] 518 552 alias = join[LHS_ALIAS] 519 553 col = join[LHS_JOIN_COL] … … 524 558 # adjustment here. We don't do this unless needed because it's less 525 559 # efficient at the database level. 526 self. alias_map[joins[0]][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER560 self.promote_alias(joins[0]) 527 561 528 562 self.where.add([alias, col, orig_field, lookup_type, value], 529 563 connection) 530 564 if negate: 531 if last:532 self. alias_map[last[0]][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER565 if seen_aliases: 566 self.promote_alias(last[0]) 533 567 self.where.negate() 534 568 django/branches/queryset-refactor/django/db/models/sql/where.py
r6120 r6121 65 65 format = '(%s)' 66 66 else: 67 sql = self.make_atom(child) 68 params = child[2].get_db_prep_lookup(child[3], child[4]) 69 format = '%s' 70 result.append(format % sql) 71 result_params.extend(params) 67 try: 68 sql = self.make_atom(child) 69 params = child[2].get_db_prep_lookup(child[3], child[4]) 70 format = '%s' 71 except EmptyResultSet: 72 if node.negated: 73 # If this is a "not" atom, being empty means it has no 74 # effect on the result, so we can ignore it. 75 continue 76 raise 77 if sql: 78 result.append(format % sql) 79 result_params.extend(params) 72 80 conn = ' %s ' % node.connection 73 81 return conn.join(result), result_params django/branches/queryset-refactor/django/utils/tree.py
r6115 r6121 21 21 self.subtree_parents = [] 22 22 self.negated = False 23 24 def __str__(self): 25 return '(%s: %s)' % (self.connection, ', '.join([str(c) for c in 26 self.children])) 23 27 24 28 def __deepcopy__(self, memodict): … … 60 64 self.connection = conn_type 61 65 if self.connection == conn_type: 62 if isinstance(node, Node) and node.connection == conn_type: 66 if isinstance(node, Node) and (node.connection == conn_type 67 or len(node) == 1): 63 68 self.children.extend(node.children) 64 69 else: django/branches/queryset-refactor/tests/modeltests/lookup/models.py
r5876 r6121 259 259 Traceback (most recent call last): 260 260 ... 261 TypeError: Cannot resolve keyword 'headline__starts' into field. Choices are: id, headline, pub_date261 TypeError: Join on field 'headline' not permitted. 262 262 263 263 # Create some articles with a bit more interesting headlines for testing field lookups: django/branches/queryset-refactor/tests/regressiontests/queries/models.py
r6113 r6121 112 112 [<Item: two>] 113 113 114 Bug #2080 115 # FIXME: Still problematic: the join needs to be "left outer" on the reverse 116 # fk, but the individual joins only need to be inner. 117 # >>> Author.objects.filter(Q(name='a3') | Q(item__name='one')) 118 # [<Author: a3>] 114 Bug #2080, #3592 115 >>> Author.objects.filter(Q(name='a3') | Q(item__name='one')) 116 [<Author: a1>, <Author: a3>] 119 117 120 118 Bug #2939
