summaryrefslogtreecommitdiff
path: root/tests/gis_tests
diff options
context:
space:
mode:
authorJacob Walls <jwalls@azavea.com>2023-02-02 10:23:16 -0500
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-02-07 11:48:10 +0100
commit4403432b759124aa613249373e0d2ede64ae8765 (patch)
tree120b68e4926f48b961f302a1b064066864d68daa /tests/gis_tests
parentfb77be9ae1cbca4a52948e129c32a482c2c99c10 (diff)
downloaddjango-4403432b759124aa613249373e0d2ede64ae8765.tar.gz
Fixed #33638 -- Fixed GIS lookups crash with geography fields on PostGIS.
Diffstat (limited to 'tests/gis_tests')
-rw-r--r--tests/gis_tests/geogapp/models.py10
-rw-r--r--tests/gis_tests/geogapp/tests.py58
2 files changed, 49 insertions, 19 deletions
diff --git a/tests/gis_tests/geogapp/models.py b/tests/gis_tests/geogapp/models.py
index c5c550feba..c06d4a7f8f 100644
--- a/tests/gis_tests/geogapp/models.py
+++ b/tests/gis_tests/geogapp/models.py
@@ -18,6 +18,16 @@ class City(NamedModel):
app_label = "geogapp"
+class CityUnique(NamedModel):
+ point = models.PointField(geography=True, unique=True)
+
+ class Meta:
+ required_db_features = {
+ "supports_geography",
+ "supports_geometry_field_unique_index",
+ }
+
+
class Zipcode(NamedModel):
code = models.CharField(max_length=10)
poly = models.PolygonField(geography=True)
diff --git a/tests/gis_tests/geogapp/tests.py b/tests/gis_tests/geogapp/tests.py
index ae12d26706..8565608993 100644
--- a/tests/gis_tests/geogapp/tests.py
+++ b/tests/gis_tests/geogapp/tests.py
@@ -6,12 +6,14 @@ import os
from django.contrib.gis.db import models
from django.contrib.gis.db.models.functions import Area, Distance
from django.contrib.gis.measure import D
+from django.core.exceptions import ValidationError
from django.db import NotSupportedError, connection
from django.db.models.functions import Cast
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
+from django.test.utils import CaptureQueriesContext
from ..utils import FuncTestMixin
-from .models import City, County, Zipcode
+from .models import City, CityUnique, County, Zipcode
class GeographyTest(TestCase):
@@ -38,28 +40,46 @@ class GeographyTest(TestCase):
for cities in [cities1, cities2]:
self.assertEqual(["Dallas", "Houston", "Oklahoma City"], cities)
- def test04_invalid_operators_functions(self):
+ @skipUnlessDBFeature("supports_geography", "supports_geometry_field_unique_index")
+ def test_geography_unique(self):
"""
- Exceptions are raised for operators & functions invalid on geography
- fields.
+ Cast geography fields to geometry type when validating uniqueness to
+ remove the reliance on unavailable ~= operator.
+ """
+ htown = City.objects.get(name="Houston")
+ CityUnique.objects.create(point=htown.point)
+ duplicate = CityUnique(point=htown.point)
+ msg = "City unique with this Point already exists."
+ with self.assertRaisesMessage(ValidationError, msg):
+ duplicate.validate_unique()
+
+ @skipUnlessDBFeature("supports_geography")
+ def test_operators_functions_unavailable_for_geography(self):
+ """
+ Geography fields are cast to geometry if the relevant operators or
+ functions are not available.
"""
- if not connection.ops.postgis:
- self.skipTest("This is a PostGIS-specific test.")
- # Only a subset of the geometry functions & operator are available
- # to PostGIS geography types. For more information, visit:
- # http://postgis.refractions.net/documentation/manual-1.5/ch08.html#PostGIS_GeographyFunctions
z = Zipcode.objects.get(code="77002")
- # ST_Within not available.
- with self.assertRaises(ValueError):
- City.objects.filter(point__within=z.poly).count()
- # `@` operator not available.
- with self.assertRaises(ValueError):
- City.objects.filter(point__contained=z.poly).count()
-
- # Regression test for #14060, `~=` was never really implemented for PostGIS.
+ point_field = "%s.%s::geometry" % (
+ connection.ops.quote_name(City._meta.db_table),
+ connection.ops.quote_name("point"),
+ )
+ # ST_Within.
+ qs = City.objects.filter(point__within=z.poly)
+ with CaptureQueriesContext(connection) as ctx:
+ self.assertEqual(qs.count(), 1)
+ self.assertIn(f"ST_Within({point_field}", ctx.captured_queries[0]["sql"])
+ # @ operator.
+ qs = City.objects.filter(point__contained=z.poly)
+ with CaptureQueriesContext(connection) as ctx:
+ self.assertEqual(qs.count(), 1)
+ self.assertIn(f"{point_field} @", ctx.captured_queries[0]["sql"])
+ # ~= operator.
htown = City.objects.get(name="Houston")
- with self.assertRaises(ValueError):
- City.objects.get(point__exact=htown.point)
+ qs = City.objects.filter(point__exact=htown.point)
+ with CaptureQueriesContext(connection) as ctx:
+ self.assertEqual(qs.count(), 1)
+ self.assertIn(f"{point_field} ~=", ctx.captured_queries[0]["sql"])
def test05_geography_layermapping(self):
"Testing LayerMapping support on models with geography fields."