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.
This commit is contained in:
Simon Charette 2019-08-17 10:32:56 -04:00 committed by Mariusz Felisiak
parent 92c72b68b7
commit bb9e82f274
5 changed files with 45 additions and 1 deletions

View File

@ -36,6 +36,9 @@ class BaseSpatialFeatures:
# The following properties indicate if the database backend support # The following properties indicate if the database backend support
# certain lookups (dwithin, left and right, relate, ...) # certain lookups (dwithin, left and right, relate, ...)
supports_left_right_lookups = False 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? # Does the database have raster support?
supports_raster = False supports_raster = False

View File

@ -9,3 +9,4 @@ class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures):
supports_geometry_field_introspection = False supports_geometry_field_introspection = False
supports_geometry_field_unique_index = False supports_geometry_field_unique_index = False
supports_perimeter_geodetic = True supports_perimeter_geodetic = True
supports_dwithin_distance_expr = False

View File

@ -1,6 +1,8 @@
import re import re
from django.contrib.gis.db.models.fields import BaseSpatialField 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.expressions import Expression
from django.db.models.lookups import Lookup, Transform from django.db.models.lookups import Lookup, Transform
from django.db.models.sql.query import Query from django.db.models.sql.query import Query
@ -301,7 +303,20 @@ class DistanceLookupBase(GISLookup):
@BaseSpatialField.register_lookup @BaseSpatialField.register_lookup
class DWithinLookup(DistanceLookupBase): class DWithinLookup(DistanceLookupBase):
lookup_name = 'dwithin' 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): def process_rhs(self, compiler, connection):
dist_sql, dist_params = self.process_distance(compiler, connection) dist_sql, dist_params = self.process_distance(compiler, connection)

View File

@ -28,6 +28,7 @@ class AustraliaCity(NamedModel):
"City model for Australia, using WGS84." "City model for Australia, using WGS84."
point = models.PointField() point = models.PointField()
radius = models.IntegerField(default=10000) radius = models.IntegerField(default=10000)
allowed_distance = models.FloatField(default=0.5)
class CensusZipcode(NamedModel): class CensusZipcode(NamedModel):

View File

@ -234,6 +234,30 @@ class DistanceTest(TestCase):
).filter(annotated_value=True) ).filter(annotated_value=True)
self.assertEqual(self.get_names(qs), ['77002', '77025', '77401']) 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')),
))
''' '''
============================= =============================