Django

Code

Changeset 6121

Show
Ignore:
Timestamp:
09/13/07 01:33:41 (1 year ago)
Author:
mtredinnick
Message:

Fixed some more join and lookup tests.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/queryset-refactor/django/db/models/base.py

    r5975 r6121  
    335335        param = smart_str(getattr(self, field.attname)) 
    336336        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)]) 
    339339        try: 
    340340            return q[0] 
  • django/branches/queryset-refactor/django/db/models/sql/query.py

    r6120 r6121  
    1111 
    1212from django.utils import tree 
    13 from django.db.models.sql.where import WhereNode, AND 
     13from django.db.models.sql.where import WhereNode, AND, OR 
    1414from django.db.models.sql.datastructures import Count 
    1515from django.db.models.fields import FieldDoesNotExist 
     
    234234        change_map = {} 
    235235        used = {} 
     236        first_new_join = True 
    236237        for alias in rhs.tables: 
     238            if not rhs.alias_map[alias][ALIAS_REFCOUNT]: 
     239                # An unused alias. 
     240                continue 
    237241            promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == 
    238242                    self.LOUTER) 
    239243            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 
    241247            used[new_alias] = None 
    242248            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 
    243256 
    244257        # Now relabel a copy of the rhs where-clause and add it to the current 
     
    381394        self.alias_map[alias][ALIAS_REFCOUNT] -= 1 
    382395 
     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 
    383400    def join(self, (lhs, table, lhs_col, col), always_create=False, 
    384             exclusions=(), promote=False): 
     401            exclusions=(), promote=False, outer_if_first=False): 
    385402        """ 
    386403        Returns an alias for a join between 'table' and 'lhs' on the given 
     
    399416        the alias previously existed, the join type will be promoted from INNER 
    400417        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. 
    401422        """ 
    402423        if lhs not in self.alias_map: 
     
    423444                "Must pass in lhs alias when creating a new join." 
    424445        alias, _ = self.table_alias(table, True) 
    425         join_type = promote and self.LOUTER or self.INNER 
     446        join_type = (promote or outer_if_first) and self.LOUTER or self.INNER 
    426447        join = [table, alias, join_type, lhs, lhs_col, col] 
    427448        if not lhs: 
     
    488509        alias = self.join((None, opts.db_table, None, None)) 
    489510        dupe_multis = (connection == AND) 
    490         last = None 
     511        seen_aliases = [] 
     512        done_split = not self.where 
    491513 
    492514        # FIXME: Using enumerate() here is expensive. We only need 'i' to 
     
    499521                name = target_field.name 
    500522            if joins is not None: 
     523                seen_aliases.extend(joins) 
    501524                last = joins 
    502525                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) 
    503537            else: 
    504538                # Normal field lookup must be the last field in the filter. 
    505539                if i != len(parts) - 1: 
    506                     raise TypeError("Joins on field %r not permitted." 
     540                    raise TypeError("Join on field %r not permitted." 
    507541                            % name) 
    508542 
    509543        col = target_col or target_field.column 
    510544 
    511         if target_field is opts.pk and last
     545        if target_field is opts.pk and seen_aliases
    512546            # An optimization: if the final join is against a primary key, 
    513547            # we can go back one step in the join chain and compare against 
     
    515549            # one less table join. 
    516550            self.unref_alias(alias) 
    517             join = self.alias_map[last[-1]][ALIAS_JOIN] 
     551            join = self.alias_map[seen_aliases[-1]][ALIAS_JOIN] 
    518552            alias = join[LHS_ALIAS] 
    519553            col = join[LHS_JOIN_COL] 
     
    524558            # adjustment here. We don't do this unless needed because it's less 
    525559            # efficient at the database level. 
    526             self.alias_map[joins[0]][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     560            self.promote_alias(joins[0]) 
    527561 
    528562        self.where.add([alias, col, orig_field, lookup_type, value], 
    529563                connection) 
    530564        if negate: 
    531             if last
    532                 self.alias_map[last[0]][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     565            if seen_aliases
     566                self.promote_alias(last[0]) 
    533567            self.where.negate() 
    534568 
  • django/branches/queryset-refactor/django/db/models/sql/where.py

    r6120 r6121  
    6565                    format = '(%s)' 
    6666            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) 
    7280        conn = ' %s ' % node.connection 
    7381        return conn.join(result), result_params 
  • django/branches/queryset-refactor/django/utils/tree.py

    r6115 r6121  
    2121        self.subtree_parents = [] 
    2222        self.negated = False 
     23 
     24    def __str__(self): 
     25        return '(%s: %s)' % (self.connection, ', '.join([str(c) for c in 
     26            self.children])) 
    2327 
    2428    def __deepcopy__(self, memodict): 
     
    6064            self.connection = conn_type 
    6165        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): 
    6368                self.children.extend(node.children) 
    6469            else: 
  • django/branches/queryset-refactor/tests/modeltests/lookup/models.py

    r5876 r6121  
    259259Traceback (most recent call last): 
    260260    ... 
    261 TypeError: Cannot resolve keyword 'headline__starts' into field. Choices are: id, headline, pub_date 
     261TypeError: Join on field 'headline' not permitted. 
    262262 
    263263# Create some articles with a bit more interesting headlines for testing field lookups: 
  • django/branches/queryset-refactor/tests/regressiontests/queries/models.py

    r6113 r6121  
    112112[<Item: two>] 
    113113 
    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>] 
     114Bug #2080, #3592 
     115>>> Author.objects.filter(Q(name='a3') | Q(item__name='one')) 
     116[<Author: a1>, <Author: a3>] 
    119117 
    120118Bug #2939