From cbae4d31847d75d889815bfe7c04af035f45e28d Mon Sep 17 00:00:00 2001 From: Christian von Roques Date: Sat, 12 Nov 2016 17:18:22 -0400 Subject: [PATCH] Fixed #27448 -- Switched use of functions deprecated in PostGIS 2.2. Thanks Claude Paroz and Tim Graham for reviews, and Mjumbe Wawatu Poe for the initial regression test. --- .../gis/db/backends/postgis/operations.py | 29 ++++++++++++++----- django/contrib/gis/db/models/functions.py | 7 +++-- tests/gis_tests/tests.py | 18 ++++++++++++ 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/django/contrib/gis/db/backends/postgis/operations.py b/django/contrib/gis/db/backends/postgis/operations.py index 763f376544..56bdbd6824 100644 --- a/django/contrib/gis/db/backends/postgis/operations.py +++ b/django/contrib/gis/db/backends/postgis/operations.py @@ -91,13 +91,16 @@ class PostGISDistanceOperator(PostGISOperator): template_params = self.check_raster(lookup, template_params) sql_template = self.sql_template if len(lookup.rhs) == 3 and lookup.rhs[-1] == 'spheroid': - template_params.update({'op': self.op, 'func': 'ST_Distance_Spheroid'}) + template_params.update({ + 'op': self.op, + 'func': connection.ops.spatial_function_name('DistanceSpheroid'), + }) sql_template = '%(func)s(%(lhs)s, %(rhs)s, %%s) %(op)s %(value)s' - # Using distance_spheroid requires the spheroid of the field as + # Using DistanceSpheroid requires the spheroid of the field as # a parameter. sql_params.insert(1, lookup.lhs.output_field._spheroid) else: - template_params.update({'op': self.op, 'func': 'ST_Distance_Sphere'}) + template_params.update({'op': self.op, 'func': connection.ops.spatial_function_name('DistanceSphere')}) return sql_template % template_params, sql_params return super(PostGISDistanceOperator, self).as_sql(connection, lookup, template_params, sql_params) @@ -146,11 +149,6 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): } unsupported_functions = set() - function_names = { - 'BoundingCircle': 'ST_MinimumBoundingCircle', - 'MemSize': 'ST_Mem_Size', - 'NumPoints': 'ST_NPoints', - } def __init__(self, connection): super(PostGISOperations, self).__init__(connection) @@ -197,6 +195,21 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.union = prefix + 'Union' self.unionagg = prefix + 'Union' + @cached_property + def function_names(self): + function_names = { + 'BoundingCircle': 'ST_MinimumBoundingCircle', + 'NumPoints': 'ST_NPoints', + } + if self.spatial_version < (2, 2, 0): + function_names.update({ + 'DistanceSphere': 'ST_distance_sphere', + 'DistanceSpheroid': 'ST_distance_spheroid', + 'LengthSpheroid': 'ST_length_spheroid', + 'MemSize': 'ST_mem_size', + }) + return function_names + @cached_property def spatial_version(self): """Determine the version of the PostGIS library.""" diff --git a/django/contrib/gis/db/models/functions.py b/django/contrib/gis/db/models/functions.py index fd251b63e0..1929b2f7a1 100644 --- a/django/contrib/gis/db/models/functions.py +++ b/django/contrib/gis/db/models/functions.py @@ -249,11 +249,12 @@ class Distance(DistanceResultMixin, OracleToleranceMixin, GeoFuncWithGeoParam): elif geo_field.geodetic(connection): # Geometry fields with geodetic (lon/lat) coordinates need special distance functions if self.spheroid: - self.function = 'ST_Distance_Spheroid' # More accurate, resource intensive + # DistanceSpheroid is more accurate and resource intensive than DistanceSphere + self.function = connection.ops.spatial_function_name('DistanceSpheroid') # Replace boolean param by the real spheroid of the base field self.source_expressions[2] = Value(geo_field._spheroid) else: - self.function = 'ST_Distance_Sphere' + self.function = connection.ops.spatial_function_name('DistanceSphere') return super(Distance, self).as_sql(compiler, connection) def as_oracle(self, compiler, connection): @@ -307,7 +308,7 @@ class Length(DistanceResultMixin, OracleToleranceMixin, GeoFunc): self.source_expressions.append(Value(self.spheroid)) elif geo_field.geodetic(connection): # Geometry fields with geodetic (lon/lat) coordinates need length_spheroid - self.function = 'ST_Length_Spheroid' + self.function = connection.ops.spatial_function_name('LengthSpheroid') self.source_expressions.append(Value(geo_field._spheroid)) else: dim = min(f.dim for f in self.get_source_fields() if f) diff --git a/tests/gis_tests/tests.py b/tests/gis_tests/tests.py index 65526d0cb8..59b8fcf7b5 100644 --- a/tests/gis_tests/tests.py +++ b/tests/gis_tests/tests.py @@ -94,3 +94,21 @@ class TestPostGISVersionCheck(unittest.TestCase): ops = FakePostGISOperations() with self.assertRaises(ImproperlyConfigured): ops.spatial_version + + def test_version_dependent_funcs(self): + """ + Resolve names of functions renamed and deprecated in PostGIS 2.2.0 + depending on PostGIS version. + Remove when dropping support for PostGIS 2.1. + """ + ops = FakePostGISOperations('2.2.0') + self.assertEqual(ops.spatial_function_name('DistanceSphere'), 'ST_DistanceSphere') + self.assertEqual(ops.spatial_function_name('DistanceSpheroid'), 'ST_DistanceSpheroid') + self.assertEqual(ops.spatial_function_name('LengthSpheroid'), 'ST_LengthSpheroid') + self.assertEqual(ops.spatial_function_name('MemSize'), 'ST_MemSize') + + ops = FakePostGISOperations('2.1.0') + self.assertEqual(ops.spatial_function_name('DistanceSphere'), 'ST_distance_sphere') + self.assertEqual(ops.spatial_function_name('DistanceSpheroid'), 'ST_distance_spheroid') + self.assertEqual(ops.spatial_function_name('LengthSpheroid'), 'ST_length_spheroid') + self.assertEqual(ops.spatial_function_name('MemSize'), 'ST_mem_size')