summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Charette <simon.charette@zapier.com>2019-08-17 10:32:56 -0400
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-08-23 21:28:28 +0200
commitbb9e82f2748ace292a584841ab9af8696df27f53 (patch)
treeda8acb9d8ca4685fe40fe2d1091c439bf66e0ab9
parent92c72b68b759d0b12d90e029f03a98c6a8f661fe (diff)
Fixed #29955 -- Added support for distance expression to the dwithin lookup.
This was missed when adding support to other distance lookups in refs #25499. Thanks Peter Bex for the report and Mariusz for testcases.
-rw-r--r--django/contrib/gis/db/backends/base/features.py3
-rw-r--r--django/contrib/gis/db/backends/oracle/features.py1
-rw-r--r--django/contrib/gis/db/models/lookups.py17
-rw-r--r--tests/gis_tests/distapp/models.py1
-rw-r--r--tests/gis_tests/distapp/tests.py24
5 files changed, 45 insertions, 1 deletions
diff --git a/django/contrib/gis/db/backends/base/features.py b/django/contrib/gis/db/backends/base/features.py
index 07f56eb0dc..5c25ca5ac0 100644
--- a/django/contrib/gis/db/backends/base/features.py
+++ b/django/contrib/gis/db/backends/base/features.py
@@ -36,6 +36,9 @@ class BaseSpatialFeatures:
# The following properties indicate if the database backend support
# certain lookups (dwithin, left and right, relate, ...)
supports_left_right_lookups = False
+ # Does the backend support expressions for specifying distance in the
+ # dwithin lookup?
+ supports_dwithin_distance_expr = True
# Does the database have raster support?
supports_raster = False
diff --git a/django/contrib/gis/db/backends/oracle/features.py b/django/contrib/gis/db/backends/oracle/features.py
index ece45b2623..f2ed4d3076 100644
--- a/django/contrib/gis/db/backends/oracle/features.py
+++ b/django/contrib/gis/db/backends/oracle/features.py
@@ -9,3 +9,4 @@ class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures):
supports_geometry_field_introspection = False
supports_geometry_field_unique_index = False
supports_perimeter_geodetic = True
+ supports_dwithin_distance_expr = False
diff --git a/django/contrib/gis/db/models/lookups.py b/django/contrib/gis/db/models/lookups.py
index 6d5df2c10f..f0f5e14b43 100644
--- a/django/contrib/gis/db/models/lookups.py
+++ b/django/contrib/gis/db/models/lookups.py
@@ -1,6 +1,8 @@
import re
from django.contrib.gis.db.models.fields import BaseSpatialField
+from django.contrib.gis.measure import Distance
+from django.db import NotSupportedError
from django.db.models.expressions import Expression
from django.db.models.lookups import Lookup, Transform
from django.db.models.sql.query import Query
@@ -301,7 +303,20 @@ class DistanceLookupBase(GISLookup):
@BaseSpatialField.register_lookup
class DWithinLookup(DistanceLookupBase):
lookup_name = 'dwithin'
- sql_template = '%(func)s(%(lhs)s, %(rhs)s, %%s)'
+ sql_template = '%(func)s(%(lhs)s, %(rhs)s, %(value)s)'
+
+ def process_distance(self, compiler, connection):
+ dist_param = self.rhs_params[0]
+ if (
+ not connection.features.supports_dwithin_distance_expr and
+ hasattr(dist_param, 'resolve_expression') and
+ not isinstance(dist_param, Distance)
+ ):
+ raise NotSupportedError(
+ 'This backend does not support expressions for specifying '
+ 'distance in the dwithin lookup.'
+ )
+ return super().process_distance(compiler, connection)
def process_rhs(self, compiler, connection):
dist_sql, dist_params = self.process_distance(compiler, connection)
diff --git a/tests/gis_tests/distapp/models.py b/tests/gis_tests/distapp/models.py
index fcad6fc097..1971741c54 100644
--- a/tests/gis_tests/distapp/models.py
+++ b/tests/gis_tests/distapp/models.py
@@ -28,6 +28,7 @@ class AustraliaCity(NamedModel):
"City model for Australia, using WGS84."
point = models.PointField()
radius = models.IntegerField(default=10000)
+ allowed_distance = models.FloatField(default=0.5)
class CensusZipcode(NamedModel):
diff --git a/tests/gis_tests/distapp/tests.py b/tests/gis_tests/distapp/tests.py
index d84e829868..2cdd0e8f0e 100644
--- a/tests/gis_tests/distapp/tests.py
+++ b/tests/gis_tests/distapp/tests.py
@@ -234,6 +234,30 @@ class DistanceTest(TestCase):
).filter(annotated_value=True)
self.assertEqual(self.get_names(qs), ['77002', '77025', '77401'])
+ @skipUnlessDBFeature('supports_dwithin_lookup', 'supports_dwithin_distance_expr')
+ def test_dwithin_with_expression_rhs(self):
+ # LineString of Wollongong and Adelaide coords.
+ ls = LineString(((150.902, -34.4245), (138.6, -34.9258)), srid=4326)
+ qs = AustraliaCity.objects.filter(
+ point__dwithin=(ls, F('allowed_distance')),
+ ).order_by('name')
+ self.assertEqual(
+ self.get_names(qs),
+ ['Adelaide', 'Mittagong', 'Shellharbour', 'Thirroul', 'Wollongong'],
+ )
+
+ @skipIfDBFeature('supports_dwithin_distance_expr')
+ def test_dwithin_with_expression_rhs_not_supported(self):
+ ls = LineString(((150.902, -34.4245), (138.6, -34.9258)), srid=4326)
+ msg = (
+ 'This backend does not support expressions for specifying '
+ 'distance in the dwithin lookup.'
+ )
+ with self.assertRaisesMessage(NotSupportedError, msg):
+ list(AustraliaCity.objects.filter(
+ point__dwithin=(ls, F('allowed_distance')),
+ ))
+
'''
=============================