From cf1a80fc2d19f359744a20bb6cb1f0a169ef506b Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 5 Jun 2025 16:18:56 -0400 Subject: [PATCH] Added validation to BaseSpatialFeatures.has__function. --- django/contrib/gis/db/backends/base/features.py | 8 ++++++++ django/contrib/gis/db/backends/base/operations.py | 4 ++++ tests/gis_tests/geoapp/test_functions.py | 4 ++-- tests/gis_tests/tests.py | 13 ++++++++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/django/contrib/gis/db/backends/base/features.py b/django/contrib/gis/db/backends/base/features.py index cc4ce1046b..22c90a1714 100644 --- a/django/contrib/gis/db/backends/base/features.py +++ b/django/contrib/gis/db/backends/base/features.py @@ -2,6 +2,8 @@ import re from django.contrib.gis.db import models +from .operations import BaseSpatialOperations + class BaseSpatialFeatures: gis_enabled = True @@ -107,5 +109,11 @@ class BaseSpatialFeatures: m = re.match(r"has_(\w*)_function$", name) if m: func_name = m[1] + if func_name not in BaseSpatialOperations.unsupported_functions: + raise ValueError( + f"DatabaseFeatures.has_{func_name}_function isn't valid. " + f'Is "{func_name}" missing from ' + "BaseSpatialOperations.unsupported_functions?" + ) return func_name not in self.connection.ops.unsupported_functions raise AttributeError diff --git a/django/contrib/gis/db/backends/base/operations.py b/django/contrib/gis/db/backends/base/operations.py index e97e694d1d..8351e71fbd 100644 --- a/django/contrib/gis/db/backends/base/operations.py +++ b/django/contrib/gis/db/backends/base/operations.py @@ -39,13 +39,17 @@ class BaseSpatialOperations: "AsGML", "AsKML", "AsSVG", + "AsWKB", + "AsWKT", "Azimuth", "BoundingCircle", "Centroid", "ClosestPoint", "Difference", "Distance", + "DistanceSpheroid", "Envelope", + "ForcePolygonCW", "FromWKB", "FromWKT", "GeoHash", diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index 3e881b41dc..9844905200 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -559,7 +559,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase): # Exact value depends on database and version. self.assertTrue(20 <= ptown.size <= 105) - @skipUnlessDBFeature("has_NumGeom_function") + @skipUnlessDBFeature("has_NumGeometries_function") def test_num_geom(self): # Both 'countries' only have two geometries. for c in Country.objects.annotate(num_geom=functions.NumGeometries("mpoly")): @@ -576,7 +576,7 @@ class GISFunctionsTests(FuncTestMixin, TestCase): else: self.assertEqual(1, city.num_geom) - @skipUnlessDBFeature("has_NumPoint_function") + @skipUnlessDBFeature("has_NumPoints_function") def test_num_points(self): coords = [(-95.363151, 29.763374), (-95.448601, 29.713803)] Track.objects.create(name="Foo", line=LineString(coords)) diff --git a/tests/gis_tests/tests.py b/tests/gis_tests/tests.py index ba084c636d..b852ffe789 100644 --- a/tests/gis_tests/tests.py +++ b/tests/gis_tests/tests.py @@ -1,8 +1,9 @@ import unittest from django.core.exceptions import ImproperlyConfigured -from django.db import ProgrammingError +from django.db import ProgrammingError, connection from django.db.backends.base.base import NO_DB_ALIAS +from django.test import TestCase try: from django.contrib.gis.db.backends.postgis.operations import PostGISOperations @@ -12,6 +13,16 @@ except ImportError: HAS_POSTGRES = False +class BaseSpatialFeaturesTests(TestCase): + def test_invalid_has_func_function(self): + msg = ( + 'DatabaseFeatures.has_Invalid_function isn\'t valid. Is "Invalid" ' + "missing from BaseSpatialOperations.unsupported_functions?" + ) + with self.assertRaisesMessage(ValueError, msg): + connection.features.has_Invalid_function + + if HAS_POSTGRES: class FakeConnection: