diff --git a/django/db/models/query.py b/django/db/models/query.py index a39220a1b1..25febd6d88 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -313,11 +313,10 @@ class _QuerySet(object): obj.query.distinct = true_or_false return obj - def extra(self, select=None, where=None, params=None, tables=None): + def extra(self, select=None, where=None, params=None, tables=None, + order_by=None): """ - Add extra SQL fragments to the query. These are applied more or less - verbatim (no quoting, no alias renaming, etc), so care should be taken - when using extra() with other complex filters and combinations. + Add extra SQL fragments to the query. """ assert self.query.can_filter(), \ "Cannot change a query once a slice has been taken" @@ -330,6 +329,8 @@ class _QuerySet(object): clone.query.extra_params.extend(params) if tables: clone.query.extra_tables.extend(tables) + if order_by: + clone.query.extra_order_by.extend(order_by) return clone ################### diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 35c6cc28dc..5d96ec8020 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -378,9 +378,14 @@ class Query(object): for alias in self.tables: if not self.alias_map[alias][ALIAS_REFCOUNT]: continue - name, alias, join_type, lhs, lhs_col, col = \ - self.alias_map[alias][ALIAS_JOIN] - alias_str = (alias != name and ' AS %s' % alias or '') + join = self.alias_map[alias][ALIAS_JOIN] + if join: + name, alias, join_type, lhs, lhs_col, col = join + alias_str = (alias != name and ' AS %s' % alias or '') + else: + join_type = None + alias_str = '' + name = alias if join_type: result.append('%s %s%s ON (%s.%s = %s.%s)' % (join_type, qn(name), alias_str, qn(lhs), @@ -464,8 +469,8 @@ class Query(object): def find_ordering_name(self, name, opts, alias=None, default_order='ASC'): """ - Returns the table alias (the name might not be unambiguous, the alias - will be) and column name for ordering by the given 'name' parameter. + 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) diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index c775772ada..d47792ecaa 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -289,6 +289,11 @@ Bug #2076 >>> Author.objects.order_by('extra', '-name') [, , , ] +# Using remote model default ordering can span multiple models (in this case, +# Cover is ordered by Item's default, which uses Note's default). +>>> Cover.objects.all() +[, ] + # If the remote model does not have a default ordering, we order by its 'id' # field. >>> Item.objects.order_by('creator', 'name') @@ -300,8 +305,10 @@ Bug #2076 >>> Ranking.objects.all().order_by('rank') [, , ] ->>> Cover.objects.all() -[, ] +# Ordering of extra() pieces is possible, too and you can mix extra fields and +# model fields in the ordering. +>>> Ranking.objects.extra(tables=['django_site'], order_by=['-django_site.id', 'rank']) +[, , ] Bugs #2874, #3002 >>> qs = Item.objects.select_related().order_by('note__note', 'name')