mirror of
https://github.com/django/django.git
synced 2025-07-05 18:29:11 +00:00
queryset-refactor: Undo [7220] and allow ordering on multi-valued field.
Some people will shoot themselves in the foot with this. That's bad luck. The reason we need it is because some data semantics cannot be expressed in Django's ORM and that shouldn't prevent ordering on that data. For example, filtering suburbs by a geographic region and then ordering on the suburb names. The names might not be unique outside that region, but unique inside it. Django cannot know that (you can't tell the model about it), so we trust the caller. git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7285 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
8b52e8e40e
commit
670be13986
@ -9,7 +9,12 @@ class EmptyResultSet(Exception):
|
||||
class FullResultSet(Exception):
|
||||
pass
|
||||
|
||||
class JoinError(Exception):
|
||||
class MultiJoin(Exception):
|
||||
"""
|
||||
Used by join construction code to indicate the point at which a
|
||||
multi-valued join was attempted (if the caller wants to treat that
|
||||
exceptionally).
|
||||
"""
|
||||
def __init__(self, level):
|
||||
self.level = level
|
||||
|
||||
|
@ -18,7 +18,7 @@ from django.db.models.sql.where import WhereNode, EverythingNode, AND, OR
|
||||
from django.db.models.sql.datastructures import Count
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
from django.core.exceptions import FieldError
|
||||
from datastructures import EmptyResultSet, Empty, JoinError
|
||||
from datastructures import EmptyResultSet, Empty, MultiJoin
|
||||
from constants import *
|
||||
|
||||
try:
|
||||
@ -523,11 +523,8 @@ class Query(object):
|
||||
pieces = name.split(LOOKUP_SEP)
|
||||
if not alias:
|
||||
alias = self.get_initial_alias()
|
||||
try:
|
||||
field, target, opts, joins, last = self.setup_joins(pieces, opts,
|
||||
alias, False, False)
|
||||
except JoinError:
|
||||
raise FieldError("Cannot order by many-valued field: '%s'" % name)
|
||||
field, target, opts, joins, last = self.setup_joins(pieces, opts,
|
||||
alias, False)
|
||||
alias = joins[-1]
|
||||
col = target.column
|
||||
|
||||
@ -848,7 +845,7 @@ class Query(object):
|
||||
try:
|
||||
field, target, opts, join_list, last = self.setup_joins(parts, opts,
|
||||
alias, (connector == AND), allow_many)
|
||||
except JoinError, e:
|
||||
except MultiJoin, e:
|
||||
self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]))
|
||||
return
|
||||
final = len(join_list)
|
||||
@ -1007,7 +1004,7 @@ class Query(object):
|
||||
if not allow_many and (m2m or not direct):
|
||||
for alias in joins:
|
||||
self.unref_alias(alias)
|
||||
raise JoinError(pos + 1)
|
||||
raise MultiJoin(pos + 1)
|
||||
if model:
|
||||
# The field lives on a base class of the current model.
|
||||
alias_list = []
|
||||
@ -1175,7 +1172,7 @@ class Query(object):
|
||||
name.split(LOOKUP_SEP), opts, alias, False, allow_m2m,
|
||||
True)
|
||||
self.select.append((joins[-1], target.column))
|
||||
except JoinError:
|
||||
except MultiJoin:
|
||||
raise FieldError("Invalid field name: '%s'" % name)
|
||||
|
||||
def add_ordering(self, *ordering):
|
||||
|
@ -510,12 +510,13 @@ primary key if there is no ``Meta.ordering`` specified. For example::
|
||||
|
||||
...since the ``Blog`` model has no default ordering specified.
|
||||
|
||||
You can only order by model fields that have a single value attached to them
|
||||
for each instance of the model. For example, non-relations, ``ForeignKey`` and
|
||||
``OneToOneField`` fields. Explicitly, you can't order by a ``ManyToManyField``
|
||||
or a reverse ``ForeignKey`` relation. There's no naturally correct ordering
|
||||
for many-valued fields and a lot of the alternatives are not psosible to
|
||||
express in SQL very efficiently.
|
||||
It is permissible to specify a multi-valued field to order the results by (for
|
||||
example, a ``ManyToMany`` field). Normally this won't be a sensible thing to
|
||||
do and it's really an advanced usage feature. However, if you know that your
|
||||
queryset's filtering or available data implies that there will only be one
|
||||
ordering piece of data for each of the main items you are selecting, the
|
||||
ordering may well be exactly what you want to do. Use ordering on multi-valued
|
||||
fields with care and make sure the results are what you expect.
|
||||
|
||||
**New in Django development version:** If you don't want any ordering to be
|
||||
applied to a query, not even the default ordering, call ``order_by()`` with no
|
||||
|
@ -424,11 +424,10 @@ FieldError: Infinite loop caused by ordering.
|
||||
[<Ranking: 1: a3>, <Ranking: 2: a2>, <Ranking: 3: a1>]
|
||||
|
||||
# Ordering by a many-valued attribute (e.g. a many-to-many or reverse
|
||||
# ForeignKey) doesn't make sense (there's no natural ordering).
|
||||
# ForeignKey) is legal, but the results might not make sense. That isn't
|
||||
# Django's problem. Garbage in, garbage out.
|
||||
>>> Item.objects.all().order_by('tags')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
FieldError: Cannot order by many-valued field: 'tags'
|
||||
[...]
|
||||
|
||||
# If we replace the default ordering, Django adjusts the required tables
|
||||
# automatically. Item normally requires a join with Note to do the default
|
||||
|
Loading…
x
Reference in New Issue
Block a user