Changeset 6730
- Timestamp:
- 11/28/07 22:56:09 (1 year ago)
- Files:
-
- django/branches/queryset-refactor/django/db/__init__.py (modified) (2 diffs)
- django/branches/queryset-refactor/django/db/models/fields/related.py (modified) (1 diff)
- django/branches/queryset-refactor/django/db/models/options.py (modified) (2 diffs)
- django/branches/queryset-refactor/django/db/models/query.py (modified) (4 diffs)
- django/branches/queryset-refactor/django/db/models/sql/query.py (modified) (31 diffs)
- django/branches/queryset-refactor/django/db/models/sql/where.py (modified) (9 diffs)
- django/branches/queryset-refactor/tests/modeltests/custom_columns/models.py (modified) (1 diff)
- django/branches/queryset-refactor/tests/modeltests/lookup/models.py (modified) (1 diff)
- django/branches/queryset-refactor/tests/modeltests/many_to_one/models.py (modified) (2 diffs)
- django/branches/queryset-refactor/tests/modeltests/reverse_lookup/models.py (modified) (1 diff)
- django/branches/queryset-refactor/tests/regressiontests/null_queries/models.py (modified) (2 diffs)
- django/branches/queryset-refactor/tests/regressiontests/queries/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/queryset-refactor/django/db/__init__.py
r6341 r6730 12 12 13 13 try: 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 15 15 # backends that ships with Django, so look there first. 16 16 _import_path = 'django.db.backends.' 17 17 backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 18 creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, ['']) 18 19 except 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 20 21 # distributed external to Django. So we'll try that next. 21 22 try: 22 23 _import_path = '' 23 24 backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, ['']) 25 creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, ['']) 24 26 except ImportError, e_user: 25 27 # The database backend wasn't found. Display a helpful error message … … 38 40 return __import__('%s%s.%s' % (_import_path, settings.DATABASE_ENGINE, module_name), {}, {}, ['']) 39 41 40 # We don't want to import the introspect /creation modules unless41 # someone asks for 'em, so lazily load themon demmand.42 # We don't want to import the introspect module unless someone asks for it, so 43 # lazily load it on demmand. 42 44 get_introspection_module = curry(_import_database_module, _import_path, 'introspection') 43 get_creation_module = curry(_import_database_module, _import_path, 'creation') 45 46 def get_creation_module(): 47 return creation 44 48 45 49 # 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 791 791 792 792 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] 795 798 796 799 class OneToOneRel(ManyToOneRel): django/branches/queryset-refactor/django/db/models/options.py
r6502 r6730 94 94 # Insert the given field in the order in which it was created, using 95 95 # 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. 97 98 if field.rel and isinstance(field.rel, ManyToManyRel): 98 99 self.many_to_many.insert(bisect(self.many_to_many, field), field) … … 129 130 return f 130 131 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 131 184 132 185 def get_add_permission(self): django/branches/queryset-refactor/django/db/models/query.py
r6603 r6730 25 25 class _QuerySet(object): 26 26 "Represents a lazy database lookup for a set of objects" 27 def __init__(self, model=None ):27 def __init__(self, model=None, query=None): 28 28 self.model = model 29 self.query = sql.Query(self.model, connection)29 self.query = query or sql.Query(self.model, connection) 30 30 self._result_cache = None 31 31 … … 339 339 clone.query.extra_tables.extend(tables) 340 340 if order_by: 341 clone.query.extra_order_by .extend(order_by)341 clone.query.extra_order_by = order_by 342 342 return clone 343 343 … … 349 349 if klass is None: 350 350 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()) 354 352 c.__dict__.update(kwargs) 355 353 if setup and hasattr(c, '_setup_query'): … … 461 459 462 460 class 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) 465 463 self._result_cache = [] 466 464 django/branches/queryset-refactor/django/db/models/sql/query.py
r6603 r6730 13 13 from django.utils.tree import Node 14 14 from django.utils.datastructures import SortedDict 15 from django.dispatch import dispatcher 16 from django.db.models import signals 15 17 from django.db.models.sql.where import WhereNode, AND, OR 16 18 from django.db.models.sql.datastructures import Count, Date 17 from django.db.models.fields import FieldDoesNotExist, Field 19 from django.db.models.fields import FieldDoesNotExist, Field, related 18 20 from django.contrib.contenttypes import generic 19 21 from datastructures import EmptyResultSet … … 50 52 ALIAS_REFCOUNT = 1 51 53 ALIAS_JOIN = 2 52 ALIAS_MERGE_SEP = 353 54 54 55 # How many results to expect from a cursor.execute call … … 58 59 59 60 ORDER_PATTERN = re.compile(r'\?|[-+]?\w+$') 61 ORDER_DIR = { 62 'ASC': ('ASC', 'DESC'), 63 'DESC': ('DESC', 'ASC')} 64 65 class Empty(object): 66 pass 60 67 61 68 class Query(object): … … 77 84 self.join_map = {} # Maps join_tuple to list of aliases. 78 85 self.rev_join_map = {} # Reverse of join_map. 86 self.quote_cache = {} 79 87 self.default_cols = True 80 88 … … 82 90 self.select = [] 83 91 self.tables = [] # Aliases in the order they are created. 84 self.where = WhereNode( self)92 self.where = WhereNode() 85 93 self.group_by = [] 86 94 self.having = [] … … 119 127 quoted strings specially (e.g. PostgreSQL). 120 128 """ 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 122 133 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 124 137 125 138 def clone(self, klass=None, **kwargs): … … 128 141 used by clients to update attributes after copying has taken place. 129 142 """ 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) 133 148 obj.table_map = self.table_map.copy() 134 obj.alias_map = copy.deepcopy(self.alias_map)135 149 obj.join_map = copy.deepcopy(self.join_map) 136 150 obj.rev_join_map = copy.deepcopy(self.rev_join_map) 151 obj.quote_cache = {} 137 152 obj.default_cols = self.default_cols 138 153 obj.select = self.select[:] 139 154 obj.tables = self.tables[:] 140 155 obj.where = copy.deepcopy(self.where) 141 obj. where.query = obj156 obj.group_by = self.group_by[:] 142 157 obj.having = self.having[:] 143 obj.group_by = self.group_by[:]144 158 obj.order_by = self.order_by[:] 145 159 obj.low_mark, obj.high_mark = self.low_mark, self.high_mark … … 176 190 obj.select_related = False 177 191 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(), 179 193 distinct=False) 180 194 obj.add_count_column() … … 206 220 # get_from_clause() for details. 207 221 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) 209 223 210 224 result = ['SELECT'] … … 263 277 change_map = {} 264 278 used = {} 265 first_new_join = True 279 conjunction = (connection == AND) 280 first = True 266 281 for alias in rhs.tables: 267 282 if not rhs.alias_map[alias][ALIAS_REFCOUNT]: … … 270 285 promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == 271 286 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) 278 289 used[new_alias] = None 279 290 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 287 301 288 302 # Now relabel a copy of the rhs where-clause and add it to the current … … 298 312 alias = self.join((None, self.model._meta.db_table, None, None)) 299 313 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) 301 315 elif self.where: 302 316 # rhs has an empty where clause. Make it match everything (see 303 317 # above for reasoning). 304 w = WhereNode( self)318 w = WhereNode() 305 319 alias = self.join((None, self.model._meta.db_table, None, None)) 306 320 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) 308 322 else: 309 w = WhereNode( self)323 w = WhereNode() 310 324 self.where.add(w, connection) 311 325 … … 401 415 result.append('%s %s%s ON (%s.%s = %s.%s)' 402 416 % (join_type, qn(name), alias_str, qn(lhs), 403 qn(lhs_col), qn(alias), qn(col)))417 qn(lhs_col), qn(alias), qn(col))) 404 418 else: 405 419 connector = not first and ', ' or '' … … 473 487 elif get_order_dir(field)[0] not in self.extra_select: 474 488 # 'col' is of the form 'field' or 'field1__field2' or 475 # ' field1__field2__field', etc.489 # '-field1__field2__field', etc. 476 490 for table, col, order in self.find_ordering_name(field, 477 491 self.model._meta): … … 496 510 if not alias: 497 511 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 504 516 505 517 # If we get to this point and the field is a relation to another model, 506 518 # append the default ordering for that model. 507 if joinsand opts.ordering:519 if len(joins) > 1 and opts.ordering: 508 520 results = [] 509 521 for item in opts.ordering: … … 560 572 561 573 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): 564 575 """ 565 576 Returns an alias for a join between 'table' and 'lhs' on the given … … 582 593 LOUTER join type. This is used when joining certain types of querysets 583 594 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: 590 600 lhs_table = lhs 591 is_table = (lhs is not None)601 is_table = True 592 602 else: 593 603 lhs_table = self.alias_map[lhs][ALIAS_TABLE] 594 604 is_table = False 595 605 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. 609 618 610 619 # No reuse is possible, so we need a new alias. … … 612 621 "Must pass in lhs alias when creating a new join." 613 622 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 615 627 join = [table, alias, join_type, lhs, lhs_col, col] 616 628 if not lhs: … … 619 631 join[JOIN_TYPE] = None 620 632 self.alias_map[alias][ALIAS_JOIN] = join 621 self.alias_map[alias][ALIAS_MERGE_SEP] = merge_separate622 633 self.join_map.setdefault(t_ident, []).append(alias) 623 634 self.rev_join_map[alias] = t_ident … … 678 689 opts = self.model._meta 679 690 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] 718 696 719 697 if join_list: … … 722 700 # chain and compare against the lhs of the join instead. The result 723 701 # (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] 725 703 if col == join[RHS_JOIN_COL]: 726 704 self.unref_alias(alias) … … 735 713 self.promote_alias(join_list[-1][0]) 736 714 737 self.where.add([alias, col, orig_field, lookup_type, value], 738 connection) 715 self.where.add([alias, col, field, lookup_type, value], connection) 739 716 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) 748 727 749 728 def add_q(self, q_object): … … 766 745 self.add_filter(child, q_object.connection, q_object.negated) 767 746 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 842 870 843 871 def set_limits(self, low=None, high=None): … … 961 989 962 990 # 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) 970 992 971 993 class DeleteQuery(Query): … … 1004 1026 if not isinstance(related.field, generic.GenericRelation): 1005 1027 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1006 where = WhereNode( self)1028 where = WhereNode() 1007 1029 where.add((None, related.field.m2m_reverse_name(), 1008 1030 related.field, 'in', … … 1012 1034 1013 1035 for f in cls._meta.many_to_many: 1014 w1 = WhereNode( self)1036 w1 = WhereNode() 1015 1037 if isinstance(f, generic.GenericRelation): 1016 1038 from django.contrib.contenttypes.models import ContentType … … 1019 1041 ContentType.objects.get_for_model(cls).id), AND) 1020 1042 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1021 where = WhereNode( self)1043 where = WhereNode() 1022 1044 where.add((None, f.m2m_column_name(), f, 'in', 1023 1045 pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), … … 1036 1058 """ 1037 1059 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1038 where = WhereNode( self)1060 where = WhereNode() 1039 1061 field = self.model._meta.pk 1040 1062 where.add((None, field.column, field, 'in', … … 1080 1102 """ 1081 1103 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 1082 where = WhereNode( self)1104 where = WhereNode() 1083 1105 f = self.model._meta.pk 1084 1106 where.add((None, f.column, f, 'in', … … 1142 1164 return () 1143 1165 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_list1151 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 None1156 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 construct1161 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 in1171 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 1178 1166 def get_order_dir(field, default='ASC'): 1179 1167 """ … … 1184 1172 prefix) should sort. The '-' prefix always sorts the opposite way. 1185 1173 """ 1186 dirn = {'ASC': ('ASC', 'DESC'), 'DESC': ('DESC', 'ASC')}[default]1174 dirn = ORDER_DIR[default] 1187 1175 if field[0] == '-': 1188 1176 return field[1:], dirn[1] 1189 1177 return field, dirn[0] 1190 1178 1179 def 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 1186 def 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
