def find_ordering_name(self, name, opts, alias=None, default_order='ASC', already_seen=None): """ Returns the table alias (the name might be ambiguous, the alias will not be) and column name for ordering by the given 'name' parameter. The 'name' is of the form 'field1__field2__...__fieldN'. """ name, order = get_order_dir(name, default_order) pieces = name.split(LOOKUP_SEP) field, col, alias, joins, opts = self._setup_joins(pieces, opts, alias) # If we get to this point and the field is a relation to another model, # append the default ordering for that model. if field.rel and len(joins) > 1 and opts.ordering: # Firstly, avoid infinite loops. if not already_seen: already_seen = set() join_tuple = tuple([self.query.alias_map[j].table_name for j in joins]) if join_tuple in already_seen: raise FieldError('Infinite loop caused by ordering.') already_seen.add(join_tuple) results = [] for item in opts.ordering: results.extend(self.find_ordering_name(item, opts, alias, order, already_seen)) return results col, alias = self._final_join_removal(col, alias) return [(alias, col, order)]
def get_ordering(self): """ Returns a tuple containing a list representing the SQL elements in the "order by" clause, and the list of SQL elements that need to be added to the GROUP BY clause as a result of the ordering. Also sets the ordering_aliases attribute on this instance to a list of extra aliases needed in the select. Determining the ordering SQL can change the tables we need to include, so this should be run *before* get_from_clause(). """ if self.query.extra_order_by: ordering = self.query.extra_order_by elif not self.query.default_ordering: ordering = self.query.order_by else: ordering = (self.query.order_by or self.query.model._meta.ordering or []) qn = self.quote_name_unless_alias qn2 = self.connection.ops.quote_name distinct = self.query.distinct select_aliases = self._select_aliases result = [] group_by = [] ordering_aliases = [] if self.query.standard_ordering: asc, desc = ORDER_DIR['ASC'] else: asc, desc = ORDER_DIR['DESC'] # It's possible, due to model inheritance, that normal usage might try # to include the same field more than once in the ordering. We track # the table/column pairs we use and discard any after the first use. processed_pairs = set() for field in ordering: if field == '?': result.append(self.connection.ops.random_function_sql()) continue if isinstance(field, int): if field < 0: order = desc field = -field else: order = asc result.append('%s %s' % (field, order)) group_by.append((field, [])) continue col, order = get_order_dir(field, asc) if col in self.query.aggregate_select: result.append('%s %s' % (qn(col), order)) continue if '.' in field: # This came in through an extra(order_by=...) addition. Pass it # on verbatim. table, col = col.split('.', 1) if (table, col) not in processed_pairs: elt = '%s.%s' % (qn(table), col) processed_pairs.add((table, col)) if not distinct or elt in select_aliases: result.append('%s %s' % (elt, order)) group_by.append((elt, [])) elif get_order_dir(field)[0] not in self.query.extra_select: # 'col' is of the form 'field' or 'field1__field2' or # '-field1__field2__field', etc. for table, col, order in self.find_ordering_name(field, self.query.model._meta, default_order=asc): if (table, col) not in processed_pairs: elt = '%s.%s' % (qn(table), qn2(col)) processed_pairs.add((table, col)) if distinct and elt not in select_aliases: ordering_aliases.append(elt) result.append('%s %s' % (elt, order)) group_by.append((elt, [])) else: elt = qn2(col) if distinct and col not in select_aliases: ordering_aliases.append(elt) result.append('%s %s' % (elt, order)) group_by.append(self.query.extra_select[col]) self.query.ordering_aliases = ordering_aliases return result, group_by