diff --git a/django/contrib/gis/db/backends/base/adapter.py b/django/contrib/gis/db/backends/base/adapter.py index a4aa2b656fc..9b12c4320cf 100644 --- a/django/contrib/gis/db/backends/base/adapter.py +++ b/django/contrib/gis/db/backends/base/adapter.py @@ -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 diff --git a/django/contrib/gis/db/backends/postgis/adapter.py b/django/contrib/gis/db/backends/postgis/adapter.py index 842afc9249d..57f43b667fb 100644 --- a/django/contrib/gis/db/backends/postgis/adapter.py +++ b/django/contrib/gis/db/backends/postgis/adapter.py @@ -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() diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 65c01fb6420..1ca56d60f17 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -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 diff --git a/docs/releases/1.8.1.txt b/docs/releases/1.8.1.txt index fd2140bbf0f..976179c41e7 100644 --- a/docs/releases/1.8.1.txt +++ b/docs/releases/1.8.1.txt @@ -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`). diff --git a/tests/gis_tests/distapp/tests.py b/tests/gis_tests/distapp/tests.py index b9ba7e4df80..1889f2d9f96 100644 --- a/tests/gis_tests/distapp/tests.py +++ b/tests/gis_tests/distapp/tests.py @@ -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)