Django

Code

Changeset 7462

Show
Ignore:
Timestamp:
04/25/08 12:08:28 (2 months ago)
Author:
mtredinnick
Message:

queryset-refactor: Fixed some bugs in the multi-valued filtering behaviour
introduced in [7317]. It was failing in a couple of different ways on some
complex Q() combinations.

Fixed #7047

Files:

Legend:

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

    r7461 r7462  
    747747 
    748748    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): 
    750750        """ 
    751751        Returns an alias for the join in 'connection', either reusing an 
     
    757757            lhs.lhs_col = table.col 
    758758 
    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. 
    761763 
    762764        If 'exclusions' is specified, it is something satisfying the container 
     
    780782        else: 
    781783            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 
    782790        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 
    789798 
    790799        # No reuse is possible, so we need a new alias. 
     
    864873 
    865874    def add_filter(self, filter_expr, connector=AND, negate=False, trim=False, 
    866             single_filter=False): 
     875            can_reuse=None): 
    867876        """ 
    868877        Add a single filter to the query. The 'filter_expr' is a pair: 
     
    873882        constructing nested queries). 
    874883 
    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. 
    877889        """ 
    878890        arg, value = filter_expr 
     
    903915        try: 
    904916            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
    906918        except MultiJoin, e: 
    907919            self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level])) 
     
    983995                        self.where.add(entry, AND) 
    984996                        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): 
    9871001        """ 
    9881002        Adds a Q-object to the current filter. 
     
    10011015            subtree = False 
    10021016        connector = AND 
    1003         internal = False 
     1017        if used_aliases is None: 
     1018            used_aliases = set() 
    10041019        for child in q_object.children: 
    10051020            if isinstance(child, Node): 
    10061021                self.where.start_subtree(connector) 
    1007                 self.add_q(child
     1022                self.add_q(child, used_aliases
    10081023                self.where.end_subtree() 
    10091024                if q_object.negated: 
     
    10111026            else: 
    10121027                self.add_filter(child, connector, q_object.negated, 
    1013                         single_filter=internal) 
    1014                 internal = True 
     1028                        can_reuse=used_aliases) 
    10151029            connector = q_object.connector 
    10161030        if subtree: 
     
    10181032 
    10191033    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): 
    10211035        """ 
    10221036        Compute the necessary table joins for the passage through the fields 
     
    10881102 
    10891103                    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
    10911105                    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
    10931107                    joins.extend([int_alias, alias]) 
    10941108                elif field.rel: 
     
    11341148 
    11351149                    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
    11371151                    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
    11391153                    joins.extend([int_alias, alias]) 
    11401154                else: 
     
    11541168 
    11551169                    alias = self.join((alias, table, from_col, to_col), 
    1156                             dupe_multis, joins, nullable=True
     1170                            dupe_multis, joins, nullable=True, reuse=can_reuse
    11571171                    joins.append(alias) 
    11581172 
  • django/branches/queryset-refactor/tests/regressiontests/queries/models.py

    r7455 r7462  
    219219>>> Item.objects.filter(Q(tags=t1)).filter(Q(tags=t2)) 
    220220[<Item: one>] 
     221>>> Item.objects.filter(Q(tags=t1)).filter(Q(creator__name='fred')|Q(tags=t2)) 
     222[<Item: one>] 
    221223 
    222224Each filter call is processed "at once" against a single table, so this is 
     
    224226things at once (rather than two tags). 
    225227>>> Item.objects.filter(Q(tags=t1) & Q(tags=t2)) 
     228[] 
     229>>> Item.objects.filter(Q(tags=t1), Q(creator__name='fred')|Q(tags=t2)) 
    226230[] 
    227231