Django

Code

Changeset 8644

Show
Ignore:
Timestamp:
08/28/08 00:00:23 (3 months ago)
Author:
mtredinnick
Message:

Improvements to [8608] to fix an infinite loop (for exclude(generic_relation)).
Also comes with approximately 67% less stupidity in the table joins for
filtering on generic relations.

Fixed #5937, hopefully for good, this time.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/contenttypes/generic.py

    r8616 r8644  
    158158        return None 
    159159 
    160     def extra_filters(self, pieces, pos): 
     160    def extra_filters(self, pieces, pos, negate): 
    161161        """ 
    162162        Return an extra filter to the queryset so that the results are filtered 
    163163        on the appropriate content type. 
    164164        """ 
     165        if negate: 
     166            return [] 
    165167        ContentType = get_model("contenttypes", "contenttype") 
    166168        content_type = ContentType.objects.get_for_model(self.model) 
    167169        prefix = "__".join(pieces[:pos + 1]) 
    168         return "%s__%s" % (prefix, self.content_type_field_name), content_type 
     170        return [("%s__%s" % (prefix, self.content_type_field_name), 
     171            content_type)] 
    169172 
    170173class ReverseGenericRelatedObjectsDescriptor(object): 
  • django/trunk/django/db/models/sql/query.py

    r8608 r8644  
    10591059        try: 
    10601060            field, target, opts, join_list, last, extra_filters = self.setup_joins( 
    1061                     parts, opts, alias, True, allow_many, can_reuse=can_reuse) 
     1061                    parts, opts, alias, True, allow_many, can_reuse=can_reuse, 
     1062                    negate=negate, process_extras=process_extras) 
    10621063        except MultiJoin, e: 
    1063             self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level])) 
     1064            self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]), 
     1065                    can_reuse) 
    10641066            return 
    10651067        final = len(join_list) 
     
    11971199 
    11981200    def setup_joins(self, names, opts, alias, dupe_multis, allow_many=True, 
    1199             allow_explicit_fk=False, can_reuse=None): 
     1201            allow_explicit_fk=False, can_reuse=None, negate=False, 
     1202            process_extras=True): 
    12001203        """ 
    12011204        Compute the necessary table joins for the passage through the fields 
     
    12061209        disjunctive filters). If can_reuse is not None, it's a list of aliases 
    12071210        that can be reused in these joins (nothing else can be reused in this 
    1208         case). 
     1211        case). Finally, 'negate' is used in the same sense as for add_filter() 
     1212        -- it indicates an exclude() filter, or something similar. It is only 
     1213        passed in here so that it can be passed to a field's extra_filter() for 
     1214        customised behaviour. 
    12091215 
    12101216        Returns the final field involved in the join, the target database 
     
    12721278                        ())) 
    12731279 
    1274             if hasattr(field, 'extra_filters'): 
    1275                 extra_filters.append(field.extra_filters(names, pos)) 
     1280            if process_extras and hasattr(field, 'extra_filters'): 
     1281                extra_filters.extend(field.extra_filters(names, pos, negate)) 
    12761282            if direct: 
    12771283                if m2m: 
     
    12961302                            dupe_multis, exclusions, nullable=True, 
    12971303                            reuse=can_reuse) 
    1298                     alias = self.join((int_alias, table2, from_col2, to_col2), 
    1299                             dupe_multis, exclusions, nullable=True, 
    1300                             reuse=can_reuse) 
    1301                     joins.extend([int_alias, alias]) 
     1304                    if int_alias == table2 and from_col2 == to_col2: 
     1305                        joins.append(int_alias) 
     1306                        alias = int_alias 
     1307                    else: 
     1308                        alias = self.join( 
     1309                                (int_alias, table2, from_col2, to_col2), 
     1310                                dupe_multis, exclusions, nullable=True, 
     1311                                reuse=can_reuse) 
     1312                        joins.extend([int_alias, alias]) 
    13021313                elif field.rel: 
    13031314                    # One-to-one or many-to-one field 
     
    13921403                self.dupe_avoidance[ident, name] = set([alias]) 
    13931404 
    1394     def split_exclude(self, filter_expr, prefix): 
     1405    def split_exclude(self, filter_expr, prefix, can_reuse): 
    13951406        """ 
    13961407        When doing an exclude against any kind of N-to-many relation, we need 
     
    14001411        """ 
    14011412        query = Query(self.model, self.connection) 
    1402         query.add_filter(filter_expr
     1413        query.add_filter(filter_expr, can_reuse=can_reuse
    14031414        query.set_start(prefix) 
    14041415        query.clear_ordering(True) 
    1405         self.add_filter(('%s__in' % prefix, query), negate=True, trim=True) 
     1416        self.add_filter(('%s__in' % prefix, query), negate=True, trim=True, 
     1417                can_reuse=can_reuse) 
    14061418 
    14071419    def set_limits(self, low=None, high=None): 
     
    16151627        field, col, opts, joins, last, extra = self.setup_joins( 
    16161628                start.split(LOOKUP_SEP), opts, alias, False) 
     1629        self.unref_alias(alias) 
    16171630        alias = joins[last[-1]] 
    16181631        self.select = [(alias, self.alias_map[alias][RHS_JOIN_COL])] 
  • django/trunk/tests/modeltests/generic_relations/models.py

    r8608 r8644  
    132132 
    133133# Queries across generic relations respect the content types. Even though there are two TaggedItems with a tag of "fatty", this query only pulls out the one with the content type related to Animals. 
     134>>> Animal.objects.order_by('common_name') 
     135[<Animal: Lion>, <Animal: Platypus>] 
    134136>>> Animal.objects.filter(tags__tag='fatty') 
    135137[<Animal: Platypus>] 
     138>>> Animal.objects.exclude(tags__tag='fatty') 
     139[<Animal: Lion>] 
    136140 
    137141# If you delete an object with an explicit Generic relation, the related