diff options
author | Jacob Walls <jwalls@azavea.com> | 2023-02-02 10:23:16 -0500 |
---|---|---|
committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2023-02-07 11:48:10 +0100 |
commit | 4403432b759124aa613249373e0d2ede64ae8765 (patch) | |
tree | 120b68e4926f48b961f302a1b064066864d68daa /tests/gis_tests | |
parent | fb77be9ae1cbca4a52948e129c32a482c2c99c10 (diff) | |
download | django-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.py | 10 | ||||
-rw-r--r-- | tests/gis_tests/geogapp/tests.py | 58 |
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." |