Django

Code

Changeset 6730

Show
Ignore:
Timestamp:
11/28/07 22:56:09 (1 year ago)
Author:
mtredinnick
Message:

queryset-refactor: Optimisation pass. The test suite is now within 2% of trunk and it's a fairly pathological case. Introduces a couple of test failures due to some simplification in the code. They'll be fixed later.

Files:

Legend:

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

    r6341 r6730  
    1212 
    1313try: 
    14     # Most of the time, the database backend will be one of the official  
     14    # Most of the time, the database backend will be one of the official 
    1515    # backends that ships with Django, so look there first. 
    1616    _import_path = 'django.db.backends.' 
    1717    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 
     18    creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 
    1819except ImportError, e: 
    19     # If the import failed, we might be looking for a database backend  
     20    # If the import failed, we might be looking for a database backend 
    2021    # distributed external to Django. So we'll try that next. 
    2122    try: 
    2223        _import_path = '' 
    2324        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, ['']) 
     25        creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, ['']) 
    2426    except ImportError, e_user: 
    2527        # The database backend wasn't found. Display a helpful error message 
     
    3840    return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, ['']) 
    3941 
    40 # We don't want to import the introspect/creation modules unless  
    41 # someone asks for 'em, so lazily load them on demmand. 
     42# We don't want to import the introspect module unless someone asks for it, so 
     43# lazily load it on demmand. 
    4244get_introspection_module = curry(_import_database_module, _import_path, 'introspection') 
    43 get_creation_module = curry(_import_database_module, _import_path, 'creation') 
     45 
     46def get_creation_module(): 
     47    return creation 
    4448 
    4549# We want runshell() to work the same way, but we have to treat it a 
  • django/branches/queryset-refactor/django/db/models/fields/related.py

    r5975 r6730  
    791791 
    792792    def get_related_field(self): 
    793         "Returns the Field in the 'to' object to which this relationship is tied." 
    794         return self.to._meta.get_field(self.field_name) 
     793        """ 
     794        Returns the Field in the 'to' object to which this relationship is 
     795        tied. 
     796        """ 
     797        return self.to._meta.get_field_by_name(self.field_name, True)[0] 
    795798 
    796799class OneToOneRel(ManyToOneRel): 
  • django/branches/queryset-refactor/django/db/models/options.py

    r6502 r6730  
    9494        # Insert the given field in the order in which it was created, using 
    9595        # the "creation_counter" attribute of the field. 
    96         # Move many-to-many related fields from self.fields into self.many_to_many. 
     96        # Move many-to-many related fields from self.fields into 
     97        # self.many_to_many. 
    9798        if field.rel and isinstance(field.rel, ManyToManyRel): 
    9899            self.many_to_many.insert(bisect(self.many_to_many, field), field) 
     
    129130                return f 
    130131        raise FieldDoesNotExist, '%s has no field named %r' % (self.object_name, name) 
     132 
     133    def get_field_by_name(self, name, only_direct=False): 
     134        """ 
     135        Returns the (field_object, direct, m2m), where field_object is the 
     136        Field instance for the given name, direct is True if the field exists 
     137        on this model, and m2m is True for many-to-many relations. When 
     138        'direct' is False, 'field_object' is the corresponding RelatedObject 
     139        for this field (since the field doesn't have an instance associated 
     140        with it). 
     141 
     142        If 'only_direct' is True, only forwards relations (and non-relations) 
     143        are considered in the result. 
     144 
     145        Uses a cache internally, so after the first access, this is very fast. 
     146        """ 
     147        try: 
     148            result = self._name_map.get(name) 
     149        except AttributeError: 
     150            cache = self.init_name_map() 
     151            result = cache.get(name) 
     152 
     153        if not result or (not result[1] and only_direct): 
     154            raise FieldDoesNotExist('%s has no field named %r' 
     155                    % (self.object_name, name)) 
     156        return result 
     157 
     158    def get_all_field_names(self): 
     159        """ 
     160        Returns a list of all field names that are possible for this model 
     161        (including reverse relation names). 
     162        """ 
     163        try: 
     164            cache = self._name_map 
     165        except AttributeError: 
     166            cache = self.init_name_map() 
     167        names = cache.keys() 
     168        names.sort() 
     169        return names 
     170 
     171    def init_name_map(self): 
     172        """ 
     173        Initialises the field name -> field object mapping. 
     174        """ 
     175        cache = dict([(f.name, (f, True, False)) for f in self.fields]) 
     176        cache.update([(f.name, (f, True, True)) for f in self.many_to_many]) 
     177        cache.update([(f.field.related_query_name(), (f, False, True)) 
     178                for f in self.get_all_related_many_to_many_objects()]) 
     179        cache.update([(f.field.related_query_name(), (f, False, False)) 
     180                for f in self.get_all_related_objects()]) 
     181        if app_cache_ready(): 
     182            self._name_map = cache 
     183        return cache 
    131184 
    132185    def get_add_permission(self): 
  • django/branches/queryset-refactor/django/db/models/query.py

    r6603 r6730  
    2525class _QuerySet(object): 
    2626    "Represents a lazy database lookup for a set of objects" 
    27     def __init__(self, model=None): 
     27    def __init__(self, model=None, query=None): 
    2828        self.model = model 
    29         self.query = sql.Query(self.model, connection) 
     29        self.query = query or sql.Query(self.model, connection) 
    3030        self._result_cache = None 
    3131 
     
    339339            clone.query.extra_tables.extend(tables) 
    340340        if order_by: 
    341             clone.query.extra_order_by.extend(order_by) 
     341            clone.query.extra_order_by = order_by 
    342342        return clone 
    343343 
     
    349349        if klass is None: 
    350350            klass = self.__class__ 
    351         c = klass() 
    352         c.model = self.model 
    353         c.query = self.query.clone() 
     351        c = klass(model=self.model, query=self.query.clone()) 
    354352        c.__dict__.update(kwargs) 
    355353        if setup and hasattr(c, '_setup_query'): 
     
    461459 
    462460class EmptyQuerySet(QuerySet): 
    463     def __init__(self, model=None): 
    464         super(EmptyQuerySet, self).__init__(model
     461    def __init__(self, model=None, query=None): 
     462        super(EmptyQuerySet, self).__init__(model, query
    465463        self._result_cache = [] 
    466464 
  • django/branches/queryset-refactor/django/db/models/sql/query.py

    r6603 r6730  
    1313from django.utils.tree import Node 
    1414from django.utils.datastructures import SortedDict 
     15from django.dispatch import dispatcher 
     16from django.db.models import signals 
    1517from django.db.models.sql.where import WhereNode, AND, OR 
    1618from django.db.models.sql.datastructures import Count, Date 
    17 from django.db.models.fields import FieldDoesNotExist, Field 
     19from django.db.models.fields import FieldDoesNotExist, Field, related 
    1820from django.contrib.contenttypes import generic 
    1921from datastructures import EmptyResultSet 
     
    5052ALIAS_REFCOUNT = 1 
    5153ALIAS_JOIN = 2 
    52 ALIAS_MERGE_SEP = 3 
    5354 
    5455# How many results to expect from a cursor.execute call 
     
    5859 
    5960ORDER_PATTERN = re.compile(r'\?|[-+]?\w+$') 
     61ORDER_DIR = { 
     62    'ASC': ('ASC', 'DESC'), 
     63    'DESC': ('DESC', 'ASC')} 
     64 
     65class Empty(object): 
     66    pass 
    6067 
    6168class Query(object): 
     
    7784        self.join_map = {}      # Maps join_tuple to list of aliases. 
    7885        self.rev_join_map = {}  # Reverse of join_map. 
     86        self.quote_cache = {} 
    7987        self.default_cols = True 
    8088 
     
    8290        self.select = [] 
    8391        self.tables = []    # Aliases in the order they are created. 
    84         self.where = WhereNode(self
     92        self.where = WhereNode(
    8593        self.group_by = [] 
    8694        self.having = [] 
     
    119127        quoted strings specially (e.g. PostgreSQL). 
    120128        """ 
    121         if name != self.alias_map.get(name, [name])[0]: 
     129        if name in self.quote_cache: 
     130            return self.quote_cache[name] 
     131        if name in self.alias_map and name not in self.table_map: 
     132            self.quote_cache[name] = name 
    122133            return name 
    123         return self.connection.ops.quote_name(name) 
     134        r = self.connection.ops.quote_name(name) 
     135        self.quote_cache[name] = r 
     136        return r 
    124137 
    125138    def clone(self, klass=None, **kwargs): 
     
    128141        used by clients to update attributes after copying has taken place. 
    129142        """ 
    130         if not klass: 
    131             klass = self.__class__ 
    132         obj = klass(self.model, self.connection) 
     143        obj = Empty() 
     144        obj.__class__ = klass or self.__class__ 
     145        obj.model = self.model 
     146        obj.connection = self.connection 
     147        obj.alias_map = copy.deepcopy(self.alias_map) 
    133148        obj.table_map = self.table_map.copy() 
    134         obj.alias_map = copy.deepcopy(self.alias_map) 
    135149        obj.join_map = copy.deepcopy(self.join_map) 
    136150        obj.rev_join_map = copy.deepcopy(self.rev_join_map) 
     151        obj.quote_cache = {} 
    137152        obj.default_cols = self.default_cols 
    138153        obj.select = self.select[:] 
    139154        obj.tables = self.tables[:] 
    140155        obj.where = copy.deepcopy(self.where) 
    141         obj.where.query = obj 
     156        obj.group_by = self.group_by[:] 
    142157        obj.having = self.having[:] 
    143         obj.group_by = self.group_by[:] 
    144158        obj.order_by = self.order_by[:] 
    145159        obj.low_mark, obj.high_mark = self.low_mark, self.high_mark 
     
    176190        obj.select_related = False 
    177191        if obj.distinct and len(obj.select) > 1: 
    178             obj = self.clone(CountQuery, _query=obj, where=WhereNode(self), 
     192            obj = self.clone(CountQuery, _query=obj, where=WhereNode(), 
    179193                    distinct=False) 
    180194        obj.add_count_column() 
     
    206220        # get_from_clause() for details. 
    207221        from_, f_params = self.get_from_clause() 
    208         where, w_params = self.where.as_sql(
     222        where, w_params = self.where.as_sql(qn=self.quote_name_unless_alias
    209223 
    210224        result = ['SELECT'] 
     
    263277        change_map = {} 
    264278        used = {} 
    265         first_new_join = True 
     279        conjunction = (connection == AND) 
     280        first = True 
    266281        for alias in rhs.tables: 
    267282            if not rhs.alias_map[alias][ALIAS_REFCOUNT]: 
     
    270285            promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == 
    271286                    self.LOUTER) 
    272             merge_separate = (connection == AND) 
    273             new_alias = self.join(rhs.rev_join_map[alias], exclusions=used, 
    274                     promote=promote, outer_if_first=True, 
    275                     merge_separate=merge_separate) 
    276             if self.alias_map[alias][ALIAS_REFCOUNT] == 1: 
    277                 first_new_join = False 
     287            new_alias = self.join(rhs.rev_join_map[alias], 
     288                    (conjunction and not first), used, promote, not conjunction) 
    278289            used[new_alias] = None 
    279290            change_map[alias] = new_alias 
    280  
    281         # So that we don't exclude valid results, the first join that is 
    282         # exclusive to the lhs (self) must be converted to an outer join. 
    283         for alias in self.tables[1:]: 
    284             if self.alias_map[alias][ALIAS_REFCOUNT] == 1: 
    285                 self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
    286                 break 
     291            first = False 
     292 
     293        # So that we don't exclude valid results in an "or" query combination, 
     294        # the first join that is exclusive to the lhs (self) must be converted 
     295        # to an outer join. 
     296        if not conjunction: 
     297            for alias in self.tables[1:]: 
     298                if self.alias_map[alias][ALIAS_REFCOUNT] == 1: 
     299                    self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER 
     300                    break 
    287301 
    288302        # Now relabel a copy of the rhs where-clause and add it to the current 
     
    298312                alias = self.join((None, self.model._meta.db_table, None, None)) 
    299313                pk = self.model._meta.pk 
    300                 self.where.add((alias, pk.column, pk, 'isnull', False), AND) 
     314                self.where.add([alias, pk.column, pk, 'isnull', False], AND) 
    301315        elif self.where: 
    302316            # rhs has an empty where clause. Make it match everything (see 
    303317            # above for reasoning). 
    304             w = WhereNode(self
     318            w = WhereNode(
    305319            alias = self.join((None, self.model._meta.db_table, None, None)) 
    306320            pk = self.model._meta.pk 
    307             w.add((alias, pk.column, pk, 'isnull', False), AND) 
     321            w.add([alias, pk.column, pk, 'isnull', False], AND) 
    308322        else: 
    309             w = WhereNode(self
     323            w = WhereNode(
    310324        self.where.add(w, connection) 
    311325 
     
    401415                result.append('%s %s%s ON (%s.%s = %s.%s)' 
    402416                        % (join_type, qn(name), alias_str, qn(lhs), 
    403                             qn(lhs_col), qn(alias), qn(col))) 
     417                           qn(lhs_col), qn(alias), qn(col))) 
    404418            else: 
    405419                connector = not first and ', ' or '' 
     
    473487            elif get_order_dir(field)[0] not in self.extra_select: 
    474488                # 'col' is of the form 'field' or 'field1__field2' or 
    475                 # 'field1__field2__field', etc. 
     489                # '-field1__field2__field', etc. 
    476490                for table, col, order in self.find_ordering_name(field, 
    477491                        self.model._meta): 
     
    496510        if not alias: 
    497511            alias = self.join((None, opts.db_table, None, None)) 
    498         for elt in pieces: 
    499             joins, opts, unused1, field, col, unused2 = \ 
    500                     self.get_next_join(elt, opts, alias, False) 
    501             if joins: 
    502                 alias = joins[-1] 
    503         col = col or field.column 
     512        field, target, opts, joins, unused2 = self.setup_joins(pieces, opts, 
     513                alias, False) 
     514        alias = joins[-1][-1] 
     515        col = target.column 
    504516 
    505517        # If we get to this point and the field is a relation to another model, 
    506518        # append the default ordering for that model. 
    507         if joins and opts.ordering: 
     519        if len(joins) > 1 and opts.ordering: 
    508520            results = [] 
    509521            for item in opts.ordering: 
     
    560572 
    561573    def join(self, (lhs, table, lhs_col, col), always_create=False, 
    562             exclusions=(), promote=False, outer_if_first=False, 
    563             merge_separate=False): 
     574            exclusions=(), promote=False, outer_if_first=False): 
    564575        """ 
    565576        Returns an alias for a join between 'table' and 'lhs' on the given 
     
    582593        LOUTER join type. This is used when joining certain types of querysets 
    583594        and Q-objects together. 
    584  
    585         If the 'merge_separate' parameter is True, we create a new alias if we 
    586         would otherwise reuse an alias that also had 'merge_separate' set to 
    587         True when it was created. 
    588         """ 
    589         if lhs not in self.alias_map: 
     595        """ 
     596        if lhs is None: 
     597            lhs_table = None 
     598            is_table = False 
     599        elif lhs not in self.alias_map: 
    590600            lhs_table = lhs 
    591             is_table = (lhs is not None) 
     601            is_table = True 
    592602        else: 
    593603            lhs_table = self.alias_map[lhs][ALIAS_TABLE] 
    594604            is_table = False 
    595605        t_ident = (lhs_table, table, lhs_col, col) 
    596         aliases = self.join_map.get(t_ident) 
    597         if aliases and not always_create: 
    598             for alias in aliases: 
    599                 if (alias not in exclusions and 
    600                         not (merge_separate and 
    601                             self.alias_map[alias][ALIAS_MERGE_SEP])): 
    602                     self.ref_alias(alias) 
    603                     if promote: 
    604                         self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = \ 
    605                                 self.LOUTER 
    606                     return alias 
    607             # If we get to here (no non-excluded alias exists), we'll fall 
    608             # through to creating a new alias. 
     606        if not always_create: 
     607            aliases = self.join_map.get(t_ident) 
     608            if aliases: 
     609                for alias in aliases: 
     610                    if alias not in exclusions: 
     611                        self.ref_alias(alias) 
     612                        if promote: 
     613                            self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = \ 
     614                                    self.LOUTER 
     615                        return alias 
     616                # If we get to here (no non-excluded alias exists), we'll fall 
     617                # through to creating a new alias. 
    609618 
    610619        # No reuse is possible, so we need a new alias. 
     
    612621                "Must pass in lhs alias when creating a new join." 
    613622        alias, _ = self.table_alias(table, True) 
    614         join_type = (promote or outer_if_first) and self.LOUTER or self.INNER 
     623        if promote or outer_if_first: 
     624            join_type = self.LOUTER 
     625        else: 
     626            join_type = self.INNER 
    615627        join = [table, alias, join_type, lhs, lhs_col, col] 
    616628        if not lhs: 
     
    619631            join[JOIN_TYPE] = None 
    620632        self.alias_map[alias][ALIAS_JOIN] = join 
    621         self.alias_map[alias][ALIAS_MERGE_SEP] = merge_separate 
    622633        self.join_map.setdefault(t_ident, []).append(alias) 
    623634        self.rev_join_map[alias] = t_ident 
     
    678689        opts = self.model._meta 
    679690        alias = self.join((None, opts.db_table, None, None)) 
    680         dupe_multis = (connection == AND) 
    681         join_list = [] 
    682         split = not self.where 
    683         null_point = None 
    684  
    685         # FIXME: Using enumerate() here is expensive. We only need 'i' to 
    686         # check we aren't joining against a non-joinable field. Find a 
    687         # better way to do this! 
    688         for i, name in enumerate(parts): 
    689             joins, opts, orig_field, target_field, target_col, nullable = \ 
    690                     self.get_next_join(name, opts, alias, dupe_multis) 
    691             if name == 'pk': 
    692                 name = target_field.name 
    693             if joins is not None: 
    694                 if null_point is None and nullable: 
    695                     null_point = len(join_list) 
    696                 join_list.append(joins) 
    697                 alias = joins[-1] 
    698                 if connection == OR and not split: 
    699                     # FIXME: Document what's going on and why this is needed. 
    700                     if self.alias_map[joins[0]][ALIAS_REFCOUNT] == 1: 
    701                         split = True 
    702                         self.promote_alias(joins[0]) 
    703                         all_aliases = [] 
    704                         for a in join_list: 
    705                             all_aliases.extend(a) 
    706                         for t in self.tables[1:]: 
    707                             if t in all_aliases: 
    708                                 continue 
    709                             self.promote_alias(t) 
    710                             break 
    711             else: 
    712                 # Normal field lookup must be the last field in the filter. 
    713                 if i != len(parts) - 1: 
    714                     raise TypeError("Join on field %r not permitted." 
    715                             % name) 
    716  
    717         col = target_col or target_field.column 
     691 
     692        field, target, unused, join_list, nullable = self.setup_joins(parts, 
     693                opts, alias, (connection == AND)) 
     694        col = target.column 
     695        alias = join_list[-1][-1] 
    718696 
    719697        if join_list: 
     
    722700            # chain and compare against the lhs of the join instead. The result 
    723701            # (potentially) involves one less table join. 
    724             join = self.alias_map[join_list[-1][-1]][ALIAS_JOIN] 
     702            join = self.alias_map[alias][ALIAS_JOIN] 
    725703            if col == join[RHS_JOIN_COL]: 
    726704                self.unref_alias(alias) 
     
    735713            self.promote_alias(join_list[-1][0]) 
    736714 
    737         self.where.add([alias, col, orig_field, lookup_type, value], 
    738                 connection) 
     715        self.where.add([alias, col, field, lookup_type, value], connection) 
    739716        if negate: 
    740             if join_list and null_point is not None: 
    741                 for elt in join_list[null_point:]: 
    742                     for join in elt: 
    743                         self.promote_alias(join) 
    744                 self.where.negate() 
    745                 self.where.add([alias, col, orig_field, 'isnull', True], OR) 
    746             else: 
    747                 self.where.negate() 
     717            flag = False 
     718            for pos, null in enumerate(nullable): 
     719                if not null: 
     720                    continue 
     721                flag = True 
     722                for join in join_list[pos]: 
     723                    self.promote_alias(join) 
     724            self.where.negate() 
     725            if flag: 
     726                self.where.add([alias, col, field, 'isnull', True], OR) 
    748727 
    749728    def add_q(self, q_object): 
     
    766745                self.add_filter(child, q_object.connection, q_object.negated) 
    767746 
    768     def get_next_join(self, name, opts, root_alias, dupe_multis): 
    769         """ 
    770         Compute the necessary table joins for the field called 'name'. 'opts' 
    771         is the Options class for the current model (which gives the table we 
    772         are joining to), root_alias is the alias for the table we are joining 
    773         to. If dupe_multis is True, any many-to-many or many-to-one joins will 
    774         always create a new alias (necessary for disjunctive filters). 
    775  
    776         Returns a list of aliases involved in the join, the next value for 
    777         'opts', the field instance that was matched, the new field to include 
    778         in the join, the column name on the rhs of the join and whether the 
    779         join can include NULL results. 
    780         """ 
    781         if name == 'pk': 
    782             name = opts.pk.name 
    783  
    784         field = find_field(name, opts.many_to_many, False) 
    785         if field: 
    786             # Many-to-many field defined on the current model. 
    787             remote_opts = field.rel.to._meta 
    788             int_alias = self.join((root_alias, field.m2m_db_table(), 
    789                     opts.pk.column, field.m2m_column_name()), dupe_multis) 
    790             far_alias = self.join((int_alias, remote_opts.db_table, 
    791                     field.m2m_reverse_name(), remote_opts.pk.column), 
    792                     dupe_multis, merge_separate=True) 
    793             return ([int_alias, far_alias], remote_opts, field, remote_opts.pk, 
    794                     None, field.null) 
    795  
    796         field = find_field(name, opts.get_all_related_many_to_many_objects(), 
    797                 True) 
    798         if field: 
    799             # Many-to-many field defined on the target model. 
    800             remote_opts = field.opts 
    801             field = field.field 
    802             int_alias = self.join((root_alias, field.m2m_db_table(), 
    803                     opts.pk.column, field.m2m_reverse_name()), dupe_multis) 
    804             far_alias = self.join((int_alias, remote_opts.db_table, 
    805                     field.m2m_column_name(), remote_opts.pk.column), 
    806                     dupe_multis, merge_separate=True) 
    807             # XXX: Why is the final component able to be None here? 
    808             return ([int_alias, far_alias], remote_opts, field, remote_opts.pk, 
    809                     None, True) 
    810  
    811         field = find_field(name, opts.get_all_related_objects(), True) 
    812         if field: 
    813             # One-to-many field (ForeignKey defined on the target model) 
    814             remote_opts = field.opts 
    815             field = field.field 
    816             local_field = opts.get_field(field.rel.field_name) 
    817             alias = self.join((root_alias, remote_opts.db_table, 
    818                     local_field.column, field.column), dupe_multis, 
    819                     merge_separate=True) 
    820             return ([alias], remote_opts, field, field, remote_opts.pk.column, 
    821                     True) 
    822  
    823  
    824         field = find_field(name, opts.fields, False) 
    825         if not field: 
    826             raise TypeError, \ 
    827                     ("Cannot resolve keyword '%s' into field. Choices are: %s" 
    828                             % (name, ", ".join(get_legal_fields(opts)))) 
    829  
    830         if field.rel: 
    831             # One-to-one or many-to-one field 
    832             remote_opts = field.rel.to._meta 
    833             target = field.rel.get_related_field() 
    834             alias = self.join((root_alias, remote_opts.db_table, field.column, 
    835                     target.column)) 
    836             return ([alias], remote_opts, field, target, target.column, 
    837                     field.null) 
    838  
    839         # Only remaining possibility is a normal (direct lookup) field. No 
    840         # join is required. 
    841         return None, opts, field, field, None, False 
     747    def setup_joins(self, names, opts, alias, dupe_multis): 
     748        """ 
     749        Compute the necessary table joins for the passage through the fields 
     750        given in 'names'. 'opts' is the Options class for the current model 
     751        (which gives the table we are joining to), 'alias' is the alias for the 
     752        table we are joining to. If dupe_multis is True, any many-to-many or 
     753        many-to-one joins will always create a new alias (necessary for 
     754        disjunctive filters). 
     755 
     756        Returns the final field involved in the join, the target database 
     757        column (used for any 'where' constraint), the final 'opts' value, the 
     758        list of tables joined and a list indicating whether or not each join 
     759        can be null. 
     760        """ 
     761        joins = [[alias]] 
     762        nullable = [False] 
     763        for pos, name in enumerate(names): 
     764            if name == 'pk': 
     765                name = opts.pk.name 
     766 
     767            try: 
     768                field, direct, m2m = opts.get_field_by_name(name) 
     769            except FieldDoesNotExist: 
     770                names = opts.get_all_field_names() 
     771                raise TypeError("Cannot resolve keyword %r into field. " 
     772                        "Choices are: %s" % (name, ", ".join(names))) 
     773            cached_data = opts._join_cache.get(name) 
     774            orig_opts = opts 
     775 
     776            if direct: 
     777                if m2m: 
     778                    # Many-to-many field defined on the current model. 
     779                    if cached_data: 
     780                        (table1, from_col1, to_col1, table2, from_col2, 
     781                                to_col2, opts, target) = cached_data 
     782                    else: 
     783                        table1 = field.m2m_db_table() 
     784                        from_col1 = opts.pk.column 
     785                        to_col1 = field.m2m_column_name() 
     786                        opts = field.rel.to._meta 
     787                        table2 = opts.db_table 
     788                        from_col2 = field.m2m_reverse_name() 
     789                        to_col2 = opts.pk.column 
     790                        target = opts.pk 
     791                        orig_opts._join_cache[name] = (table1, from_col1, 
     792                                to_col1, table2, from_col2, to_col2, opts, 
     793                                target) 
     794 
     795                    int_alias = self.join((alias, table1, from_col1, to_col1), 
     796                            dupe_multis) 
     797                    alias = self.join((int_alias, table2, from_col2, to_col2), 
     798                            dupe_multis) 
     799                    joins.append([int_alias, alias]) 
     800                    nullable.append(field.null) 
     801                elif field.rel: 
     802                    # One-to-one or many-to-one field 
     803                    if cached_data: 
     804                        (table, from_col, to_col, opts, target) = cached_data 
     805                    else: 
     806                        opts = field.rel.to._meta 
     807                        target = field.rel.get_related_field() 
     808                        table = opts.db_table 
     809                        from_col = field.column 
     810                        to_col = target.column 
     811                        orig_opts._join_cache[name] = (table, from_col, to_col, 
     812                                opts, target) 
     813 
     814                    alias = self.join((alias, table, from_col, to_col)) 
     815                    joins.append([alias]) 
     816                    nullable.append(field.null) 
     817                else: 
     818                    target = field 
     819                    break 
     820            else: 
     821                orig_field = field 
     822                field = field.field 
     823                nullable.append(True) 
     824                if m2m: 
     825                    # Many-to-many field defined on the target model. 
     826                    if cached_data: 
     827                        (table1, from_col1, to_col1, table2, from_col2, 
     828                                to_col2, opts, target) = cached_data 
     829                    else: 
     830                        table1 = field.m2m_db_table() 
     831                        from_col1 = opts.pk.column 
     832                        to_col1 = field.m2m_reverse_name() 
     833                        opts = orig_field.opts 
     834                        table2 = opts.db_table 
     835                        from_col2 = field.m2m_column_name() 
     836                        to_col2 = opts.pk.column 
     837                        target = opts.pk 
     838                        orig_opts._join_cache[name] = (table1, from_col1, 
     839                                to_col1, table2, from_col2, to_col2, opts, 
     840                                target) 
     841 
     842                    int_alias = self.join((alias, table1, from_col1, to_col1), 
     843                            dupe_multis) 
     844                    alias = self.join((int_alias, table2, from_col2, to_col2), 
     845                            dupe_multis) 
     846                    joins.append([int_alias, alias]) 
     847                else: 
     848                    # One-to-many field (ForeignKey defined on the target model) 
     849                    if cached_data: 
     850                        (table, from_col, to_col, opts, target) = cached_data 
     851                    else: 
     852                        local_field = opts.get_field_by_name( 
     853                                field.rel.field_name)[0] 
     854                        opts = orig_field.opts 
     855                        table = opts.db_table 
     856                        from_col = local_field.column 
     857                        to_col = field.column 
     858                        target = opts.pk 
     859                        orig_opts._join_cache[name] = (table, from_col, to_col, 
     860                                opts, target) 
     861 
     862                    alias = self.join((alias, table, from_col, to_col), 
     863                            dupe_multis) 
     864                    joins.append([alias]) 
     865 
     866        if pos != len(names) - 1: 
     867            raise TypeError("Join on field %r not permitted." % name) 
     868 
     869        return field, target, opts, joins, nullable 
    842870 
    843871    def set_limits(self, low=None, high=None): 
     
    961989 
    962990        # The MULTI case. 
    963         def it(): 
    964             while 1: 
    965                 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    966                 if not rows: 
    967                     raise StopIteration 
    968                 yield rows 
    969         return it() 
     991        return results_iter(cursor) 
    970992 
    971993class DeleteQuery(Query): 
     
    10041026            if not isinstance(related.field, generic.GenericRelation): 
    10051027                for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    1006                     where = WhereNode(self
     1028                    where = WhereNode(
    10071029                    where.add((None, related.field.m2m_reverse_name(), 
    10081030                            related.field, 'in', 
     
    10121034 
    10131035        for f in cls._meta.many_to_many: 
    1014             w1 = WhereNode(self
     1036            w1 = WhereNode(
    10151037            if isinstance(f, generic.GenericRelation): 
    10161038                from django.contrib.contenttypes.models import ContentType 
     
    10191041                        ContentType.objects.get_for_model(cls).id), AND) 
    10201042            for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    1021                 where = WhereNode(self
     1043                where = WhereNode(
    10221044                where.add((None, f.m2m_column_name(), f, 'in', 
    10231045                        pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), 
     
    10361058        """ 
    10371059        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    1038             where = WhereNode(self
     1060            where = WhereNode(
    10391061            field = self.model._meta.pk 
    10401062            where.add((None, field.column, field, 'in', 
     
    10801102        """ 
    10811103        for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 
    1082             where = WhereNode(self
     1104            where = WhereNode(
    10831105            f = self.model._meta.pk 
    10841106            where.add((None, f.column, f, 'in', 
     
    11421164        return () 
    11431165 
    1144 def find_field(name, field_list, related_query): 
    1145     """ 
    1146     Finds a field with a specific name in a list of field instances. 
    1147     Returns None if there are no matches, or several matches. 
    1148     """ 
    1149     if related_query: 
    1150         matches = [f for f in field_list 
    1151                 if f.field.related_query_name() == name] 
    1152     else: 
    1153         matches = [f for f in field_list if f.name == name] 
    1154     if len(matches) != 1: 
    1155         return None 
    1156     return matches[0] 
    1157  
    1158 def field_choices(field_list, related_query): 
    1159     """ 
    1160     Returns the names of the field objects in field_list. Used to construct 
    1161     readable error messages. 
    1162     """ 
    1163     if related_query: 
    1164         return [f.field.related_query_name() for f in field_list] 
    1165     else: 
    1166         return [f.name for f in field_list] 
    1167  
    1168 def get_legal_fields(opts): 
    1169     """ 
    1170     Returns a list of fields that are valid at this point in the query. Used in 
    1171     error reporting. 
    1172     """ 
    1173     return (field_choices(opts.many_to_many, False) 
    1174             + field_choices( opts.get_all_related_many_to_many_objects(), True) 
    1175             + field_choices(opts.get_all_related_objects(), True) 
    1176             + field_choices(opts.fields, False)) 
    1177  
    11781166def get_order_dir(field, default='ASC'): 
    11791167    """ 
     
    11841172    prefix) should sort. The '-' prefix always sorts the opposite way. 
    11851173    """ 
    1186     dirn = {'ASC': ('ASC', 'DESC'), 'DESC': ('DESC', 'ASC')}[default] 
     1174    dirn = ORDER_DIR[default] 
    11871175    if field[0] == '-': 
    11881176        return field[1:], dirn[1] 
    11891177    return field, dirn[0] 
    11901178 
     1179def results_iter(cursor): 
     1180    while 1: 
     1181        rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     1182        if not rows: 
     1183            raise StopIteration 
     1184        yield rows 
     1185 
     1186def setup_join_cache(sender): 
     1187    """ 
     1188    The information needed to join between model fields is something that is 
     1189    invariant over the life of the model, so we cache it in the model's Options 
     1190    class, rather than recomputing it all the time. 
     1191 
     1192    This method initialises the (empty) cache when the model is created. 
     1193    """ 
     1194