Ticket #9368: query_cleanup.diff
File query_cleanup.diff, 19.8 KB (added by , 16 years ago) |
---|
-
django/db/models/sql/query.py
197 197 obj._setup_query() 198 198 return obj 199 199 200 def get_base_fields(self): 201 """ 202 Returns a list of fields on the base object (i.e., not related objects) 203 that are retrieved by this query. In the result row, these columns are 204 row[len(self.extra_select):len(self.extra_select)+len(self.get_base_fields())] 205 """ 206 if self.select_fields: 207 return self.select_fields 208 else: 209 return self.model._meta.fields 210 200 211 def results_iter(self): 201 212 """ 202 213 Returns an iterator over the results from executing this query. … … 210 221 # We only set this up here because 211 222 # related_select_fields isn't populated until 212 223 # execute_sql() has been called. 213 if self.select_fields: 214 fields = self.select_fields + self.related_select_fields 215 else: 216 fields = self.model._meta.fields 224 fields = self.get_base_fields() 225 if self.related_select_fields: 226 fields = fields + self.related_select_fields 217 227 row = self.resolve_columns(row, fields) 218 228 yield row 219 229 … … 435 445 if self.select: 436 446 for col in self.select: 437 447 if isinstance(col, (list, tuple)): 438 r = '%s.%s' % (qn(col[0]), qn(col[1])) 439 if with_aliases and col[1] in col_aliases: 440 c_alias = 'Col%d' % len(col_aliases) 441 result.append('%s AS %s' % (r, c_alias)) 442 aliases.add(c_alias) 443 col_aliases.add(c_alias) 444 else: 445 result.append(r) 446 aliases.add(r) 447 col_aliases.add(col[1]) 448 result.append(self._get_column( 449 table_alias=col[0],column_name=col[1], 450 with_aliases=with_aliases, 451 col_aliases=col_aliases, 452 aliases=aliases)) 448 453 else: 449 454 result.append(col.as_sql(quote_func=qn)) 450 455 if hasattr(col, 'alias'): 451 456 aliases.add(col.alias) 452 457 col_aliases.add(col.alias) 453 458 elif self.default_cols: 454 cols, new_aliases = self.get_default_columns(with_aliases, 455 col_aliases) 456 result.extend(cols) 457 aliases.update(new_aliases) 458 for table, col in self.related_select_cols: 459 r = '%s.%s' % (qn(table), qn(col)) 460 if with_aliases and col in col_aliases: 461 c_alias = 'Col%d' % len(col_aliases) 462 result.append('%s AS %s' % (r, c_alias)) 463 aliases.add(c_alias) 464 col_aliases.add(c_alias) 465 else: 466 result.append(r) 467 aliases.add(r) 468 col_aliases.add(col) 459 result.extend(self.get_base_columns(with_aliases, col_aliases, aliases)) 460 461 for table, column_name in self.related_select_cols: 462 result.append(self._get_column(table, column_name, 463 with_aliases, col_aliases, aliases)) 469 464 470 465 self._select_aliases = aliases 471 466 return result 472 467 473 def get_default_columns(self, with_aliases=False, col_aliases=None, 474 start_alias=None, opts=None, as_pairs=False): 468 def _get_table_alias(self, model, seen, root_pk): 475 469 """ 476 Computes the default columns for selecting every field in the base 477 model. 470 Gets a table alias for the given model. Seen is a dict 471 mapping already-used models to table aliases, and may be updated 472 as a side effect. 473 """ 474 try: 475 return seen[model] 476 except KeyError: 477 alias = self.join((seen[None], model._meta.db_table, 478 root_pk, model._meta.pk.column)) 479 seen[model] = alias 480 return alias 481 482 def get_base_columns(self, with_aliases, col_aliases, aliases): 483 """ 484 Computes the columns to select on the base model. By default, this includes all 485 fields. 478 486 479 Returns a list of strings, quoted appropriately for use in SQL 480 directly, as well as a set of aliases used in the select statement (if 481 'as_pairs' is True, returns a list of (alias, col_name) pairs instead 482 of strings as the first component and None as the second component). 487 Returns a list of strings, quoted appropriately for use in SQL directly. 488 As a side effect, updates the aliases and col_aliases sets passed in as an argument. 489 """ 490 result = [] 491 opts = self.model._meta 492 root_pk = opts.pk.column 493 seen = {None: self.tables[0]} 494 495 for field, model in opts.get_fields_with_model(): 496 table_alias = self._get_table_alias(model, seen, root_pk) 497 result.append(self._get_column(table_alias, field.column, 498 with_aliases, col_aliases, aliases)) 499 500 return result 501 502 def get_related_columns(self, start_alias, opts): 483 503 """ 484 result = [] 485 if opts is None: 486 opts = self.model._meta 487 if start_alias: 488 table_alias = start_alias 489 else: 490 table_alias = self.tables[0] 504 Computes the default columns for selecting every field for a related 505 model (described by opts). The related field columns currently cannot be 506 customized per-query. 507 508 Returns a list of (alias, field) pairs, where alias is a string quoted 509 appropriately for use in SQL directly. 510 """ 511 result = [] 512 seen = {None: start_alias} 491 513 root_pk = opts.pk.column 492 seen = {None: table_alias} 493 qn = self.quote_name_unless_alias 494 qn2 = self.connection.ops.quote_name 495 aliases = set() 514 496 515 for field, model in opts.get_fields_with_model(): 497 try: 498 alias = seen[model] 499 except KeyError: 500 alias = self.join((table_alias, model._meta.db_table, 501 root_pk, model._meta.pk.column)) 502 seen[model] = alias 503 if as_pairs: 504 result.append((alias, field.column)) 505 continue 506 if with_aliases and field.column in col_aliases: 507 c_alias = 'Col%d' % len(col_aliases) 508 result.append('%s.%s AS %s' % (qn(alias), 509 qn2(field.column), c_alias)) 510 col_aliases.add(c_alias) 511 aliases.add(c_alias) 512 else: 513 r = '%s.%s' % (qn(alias), qn2(field.column)) 514 result.append(r) 515 aliases.add(r) 516 if with_aliases: 517 col_aliases.add(field.column) 518 if as_pairs: 519 return result, None 520 return result, aliases 516 result.append((self._get_table_alias(model, seen, root_pk), field.column)) 517 518 return result 521 519 520 def _get_column(self, table_alias, column_name, with_aliases, col_aliases, aliases): 521 """ 522 Returns a string for a SQL select statement, containing a column name 523 as well as a column alias if necessary. 524 525 The col_aliases and aliases sets are modified as a side effect. 526 """ 527 r = '%s.%s' % (self.quote_name_unless_alias(table_alias), self.connection.ops.quote_name(column_name)) 528 if with_aliases and column_name in col_aliases: 529 c_alias = 'Col%d' % len(col_aliases) 530 531 col_aliases.add(c_alias) 532 aliases.add(c_alias) 533 534 return '%s AS %s' % (r, c_alias) 535 else: 536 aliases.add(r) 537 if with_aliases: 538 col_aliases.add(column_name) 539 540 return r 541 522 542 def get_from_clause(self): 523 543 """ 524 544 Returns a list of strings that are joined together to go after the … … 1046 1066 f.rel.get_related_field().column), 1047 1067 exclusions=used.union(avoid), promote=promote) 1048 1068 used.add(alias) 1049 self.related_select_cols.extend(self.get_default_columns(1050 start_alias=alias, opts=f.rel.to._meta, as_pairs=True)[0])1069 new_related_cols = self.get_related_columns(start_alias=alias, opts=f.rel.to._meta) 1070 self.related_select_cols.extend(new_related_cols) 1051 1071 self.related_select_fields.extend(f.rel.to._meta.fields) 1052 1072 if restricted: 1053 1073 next = requested.get(f.name, {}) … … 1529 1549 True) 1530 1550 final_alias = joins[-1] 1531 1551 col = target.column 1552 1553 # When name ends with something like fk_id, setup_joins 1554 # will add an unnecessary join on the fk. In this case, we undo the 1555 # last join, and just select fk_id on the previous model. 1532 1556 if len(joins) > 1: 1533 1557 join = self.alias_map[final_alias] 1534 1558 if col == join[RHS_JOIN_COL]: -
django/db/models/base.py
265 265 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] 266 266 signals.post_init.send(sender=self.__class__, instance=self) 267 267 268 @classmethod 269 def _new_from_select_values(cls, vals, fields): 270 """ 271 Creates a new instance of this model class. vals is a list of columns 272 (e.g., as retrieved by a select query), and fields is a parallel list 273 of corresponding field objects. 274 275 The class will be instantiated as if calling __init__ with kwargs 276 fields[i]=vals[i] for all i. 277 278 However, it may call __init__ using positional arguments when all 279 fields are provided. 280 """ 281 282 # we use pointer-equality to make the test faster in the normal case 283 if fields is cls._meta.fields: 284 return cls(*vals) # the comments for __init__ indicate this is faster 285 else: 286 return cls(**dict(zip([f.attname for f in fields], vals))) 287 268 288 def __repr__(self): 269 289 return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self))) 270 290 -
django/db/models/query.py
272 272 max_depth = self.query.max_depth 273 273 extra_select = self.query.extra_select.keys() 274 274 index_start = len(extra_select) 275 276 fields = self.query.get_base_fields() 277 model = self.model 278 275 279 for row in self.query.results_iter(): 276 280 if fill_cache: 277 obj, _ = get_cached_row( self.model, row, index_start,278 max_depth, requested=requested )281 obj, _ = get_cached_row(model, row, index_start, 282 max_depth, requested=requested, fields=fields) 279 283 else: 280 obj = self.model(*row[index_start:])284 obj = model._new_from_select_values(row[index_start:], fields) 281 285 for i, k in enumerate(extra_select): 282 286 setattr(obj, k, row[i]) 283 287 yield obj … … 683 687 field_names = [f.attname for f in self.model._meta.fields] 684 688 685 689 self.query.add_fields(field_names, False) 686 self.query.default_cols = False687 690 self.field_names = field_names 688 691 689 692 def _clone(self, klass=None, setup=False, **kwargs): … … 788 791 789 792 790 793 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, 791 requested=None ):794 requested=None, fields=None): 792 795 """ 793 796 Helper function that recursively returns an object with the specified 794 797 related attributes already populated. … … 797 800 # We've recursed deeply enough; stop now. 798 801 return None 799 802 803 if fields is None: 804 fields = klass._meta.fields 805 800 806 restricted = requested is not None 801 index_end = index_start + len( klass._meta.fields)802 fields = row[index_start:index_end]803 if not [x for x in fields if x is not None]:807 index_end = index_start + len(fields) 808 values = row[index_start:index_end] 809 if not [x for x in values if x is not None]: 804 810 # If we only have a list of Nones, there was not related object. 805 811 obj = None 806 812 else: 807 obj = klass (*fields)813 obj = klass._new_from_select_values(values, fields) 808 814 for f in klass._meta.fields: 809 815 if not select_related_descend(f, restricted, requested): 810 816 continue -
django/contrib/gis/db/models/sql/query.py
66 66 # This loop customized for GeoQuery. 67 67 for col, field in izip(self.select, self.select_fields): 68 68 if isinstance(col, (list, tuple)): 69 r = self.get_field_select(field, col[0]) 70 if with_aliases and col[1] in col_aliases: 71 c_alias = 'Col%d' % len(col_aliases) 72 result.append('%s AS %s' % (r, c_alias)) 73 aliases.add(c_alias) 74 col_aliases.add(c_alias) 75 else: 76 result.append(r) 77 aliases.add(r) 78 col_aliases.add(col[1]) 69 result.append(self._get_column_with_field( 70 table_alias=col[0],column_name=col[1],field=field, 71 with_aliases=with_aliases, 72 col_aliases=col_aliases, aliases=aliases)) 79 73 else: 80 74 result.append(col.as_sql(quote_func=qn)) 81 75 if hasattr(col, 'alias'): 82 76 aliases.add(col.alias) 83 77 col_aliases.add(col.alias) 84 78 elif self.default_cols: 85 cols, new_aliases = self.get_default_columns(with_aliases, 86 col_aliases) 87 result.extend(cols) 88 aliases.update(new_aliases) 79 result.extend(self.get_base_columns(with_aliases, col_aliases, aliases)) 89 80 # This loop customized for GeoQuery. 90 81 if not self.aggregate: 91 82 for (table, col), field in izip(self.related_select_cols, self.related_select_fields): 92 r = self.get_field_select(field, table) 93 if with_aliases and col in col_aliases: 94 c_alias = 'Col%d' % len(col_aliases) 95 result.append('%s AS %s' % (r, c_alias)) 96 aliases.add(c_alias) 97 col_aliases.add(c_alias) 98 else: 99 result.append(r) 100 aliases.add(r) 101 col_aliases.add(col) 83 result.append(self._get_column_with_field(table, col, field, 84 with_aliases, col_aliases, aliases)) 102 85 103 86 self._select_aliases = aliases 104 87 return result 105 88 106 def get_default_columns(self, with_aliases=False, col_aliases=None, 107 start_alias=None, opts=None, as_pairs=False): 89 def _get_column_with_field(self, table_alias, column_name, field, with_aliases, col_aliases, aliases): 108 90 """ 109 Computes the default columns for selecting every field in the base 110 model. 91 Returns a string for a SQL select statement, containing a column name 92 as well as a column alias if necessary. 93 94 The col_aliases and aliases sets are modified as a side effect. 95 """ 96 r = self.get_field_select(field, table_alias) 97 98 if with_aliases and column_name in col_aliases: 99 c_alias = 'Col%d' % len(col_aliases) 100 101 col_aliases.add(c_alias) 102 aliases.add(c_alias) 103 104 return '%s AS %s' % (r, c_alias) 105 else: 106 aliases.add(r) 107 if with_aliases: 108 col_aliases.add(column_name) 109 110 return r 111 111 112 Returns a list of strings, quoted appropriately for use in SQL 113 directly, as well as a set of aliases used in the select statement. 114 115 This routine is overridden from Query to handle customized selection of 116 geometry columns. 112 def get_base_columns(self, with_aliases, col_aliases, aliases): 117 113 """ 118 result = [] 119 if opts is None: 120 opts = self.model._meta 121 if start_alias: 122 table_alias = start_alias 123 else: 124 table_alias = self.tables[0] 125 root_pk = self.model._meta.pk.column 126 seen = {None: table_alias} 127 aliases = set() 114 Computes the columns to select on the base model. By default, this includes all 115 fields. 116 117 Returns a list of strings, quoted appropriately for use in SQL directly. 118 As a side effect, updates the aliases and col_aliases sets passed in as an argument. 119 120 This routine is overridden from Query to handle customized selection of 121 geometry columns. 122 """ 123 result = [] 124 opts = self.model._meta 125 root_pk = opts.pk.column 126 seen = {None: self.tables[0]} 127 128 128 for field, model in opts.get_fields_with_model(): 129 try: 130 alias = seen[model] 131 except KeyError: 132 alias = self.join((table_alias, model._meta.db_table, 133 root_pk, model._meta.pk.column)) 134 seen[model] = alias 135 if as_pairs: 136 result.append((alias, field.column)) 137 continue 138 # This part of the function is customized for GeoQuery. We 139 # see if there was any custom selection specified in the 140 # dictionary, and set up the selection format appropriately. 141 field_sel = self.get_field_select(field, alias) 142 if with_aliases and field.column in col_aliases: 143 c_alias = 'Col%d' % len(col_aliases) 144 result.append('%s AS %s' % (field_sel, c_alias)) 145 col_aliases.add(c_alias) 146 aliases.add(c_alias) 147 else: 148 r = field_sel 149 result.append(r) 150 aliases.add(r) 151 if with_aliases: 152 col_aliases.add(field.column) 153 if as_pairs: 154 return result, None 155 return result, aliases 129 table_alias = self._get_table_alias(model, seen, root_pk) 130 result.append(self._get_column_with_field(table_alias, field.column, field, 131 with_aliases, col_aliases, aliases)) 132 133 return result 156 134 157 135 def get_ordering(self): 158 136 """