Fixed #24615 -- ordering by expression not part of SELECT

Fixed queries where an expression was used in order_by() but the
expression wasn't in the query's select clause (for example the
expression could be masked by .values() call)

Thanks to Trac alias MattBlack85 for the report.
This commit is contained in:
Anssi Kääriäinen 2015-04-15 08:46:19 +03:00 committed by Claude Paroz
parent bcf700b4e3
commit fb5c7748da
5 changed files with 25 additions and 1 deletions

View File

@ -12,5 +12,8 @@ class WKTAdapter(object):
return False
return self.wkt == other.wkt and self.srid == other.srid
def __hash__(self):
return hash((self.wkt, self.srid))
def __str__(self):
return self.wkt

View File

@ -28,6 +28,9 @@ class PostGISAdapter(object):
return False
return (self.ewkb == other.ewkb) and (self.srid == other.srid)
def __hash__(self):
return hash((self.ewkb, self.srid))
def __str__(self):
return self.getquoted()

View File

@ -262,10 +262,17 @@ class SQLCompiler(object):
descending = True if order == 'DESC' else False
if col in self.query.annotation_select:
# Reference to expression in SELECT clause
order_by.append((
OrderBy(Ref(col, self.query.annotation_select[col]), descending=descending),
True))
continue
if col in self.query.annotations:
# References to an expression which is masked out of the SELECT clause
order_by.append((
OrderBy(self.query.annotations[col], descending=descending),
False))
continue
if '.' in field:
# This came in through an extra(order_by=...) addition. Pass it

View File

@ -49,3 +49,7 @@ Bugfixes
remove usage of referencing views by dotted path in
:func:`~django.conf.urls.url` which is deprecated in Django 1.8
(:ticket:`24635`).
* Fixed queries where an expression was referenced in ``order_by()``, but wasn't
part of the select clause. An example query is
``qs.annotate(foo=F('field')).values('pk').order_by('foo'))`` (:ticket:`24615`).

View File

@ -1,6 +1,6 @@
from __future__ import unicode_literals
from django.contrib.gis.geos import HAS_GEOS
from django.contrib.gis.geos import HAS_GEOS, Point
from django.contrib.gis.measure import D # alias for Distance
from django.db import connection
from django.db.models import Q
@ -383,3 +383,10 @@ class DistanceTest(TestCase):
z = SouthTexasZipcode.objects.distance(htown.point).area().get(name='78212')
self.assertIsNone(z.distance)
self.assertIsNone(z.area)
@skipUnlessDBFeature("has_distance_method")
def test_distance_order_by(self):
qs = SouthTexasCity.objects.distance(Point(3, 3)).order_by(
'distance'
).values_list('name', flat=True).filter(name__in=('San Antonio', 'Pearland'))
self.assertQuerysetEqual(qs, ['San Antonio', 'Pearland'], lambda x: x)