diff --git a/django/contrib/gis/db/backends/base/operations.py b/django/contrib/gis/db/backends/base/operations.py index 1b314a9d2b..d21198dc0f 100644 --- a/django/contrib/gis/db/backends/base/operations.py +++ b/django/contrib/gis/db/backends/base/operations.py @@ -72,10 +72,7 @@ class BaseSpatialOperations: backend. """ def transform_value(value, field): - return ( - not (value is None or value.srid == field.srid) and - self.connection.features.supports_transform - ) + return value is not None and value.srid != field.srid if hasattr(value, 'as_sql'): return ( diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt index 1fb73c9c7f..7ab3c81415 100644 --- a/docs/ref/contrib/gis/tutorial.txt +++ b/docs/ref/contrib/gis/tutorial.txt @@ -610,6 +610,8 @@ of a queryset:: The ``contains`` and ``intersects`` lookups are just a subset of the available queries -- the :doc:`db-api` documentation has more. +.. _automatic-spatial-transformations: + Automatic Spatial Transformations --------------------------------- When doing spatial queries, GeoDjango automatically transforms diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index 37bfb48d89..cd1970f08f 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -472,6 +472,10 @@ Miscellaneous option. Add this to your existing migrations or accept an auto-generated migration for fields that use it. +* Performing queries that require :ref:`automatic spatial transformations + ` now raises ``NotImplementedError`` + on MySQL instead of silently using non-transformed geometries. + .. _deprecated-features-2.0: Features deprecated in 2.0 diff --git a/tests/gis_tests/geoapp/test_functions.py b/tests/gis_tests/geoapp/test_functions.py index ccb0dbb937..9d17a625ee 100644 --- a/tests/gis_tests/geoapp/test_functions.py +++ b/tests/gis_tests/geoapp/test_functions.py @@ -270,7 +270,7 @@ class GISFunctionsTests(TestCase): def test_area_with_regular_aggregate(self): # Create projected country objects, for this test to work on all backends. for c in Country.objects.all(): - CountryWebMercator.objects.create(name=c.name, mpoly=c.mpoly) + CountryWebMercator.objects.create(name=c.name, mpoly=c.mpoly.transform(3857, clone=True)) # Test in projected coordinate system qs = CountryWebMercator.objects.annotate(area_sum=Sum(functions.Area('mpoly'))) # Some backends (e.g. Oracle) cannot group by multipolygon values, so diff --git a/tests/gis_tests/geogapp/fixtures/initial.json b/tests/gis_tests/geogapp/fixtures/initial.json index 2324bcdc16..f0f0374d47 100644 --- a/tests/gis_tests/geogapp/fixtures/initial.json +++ b/tests/gis_tests/geogapp/fixtures/initial.json @@ -68,7 +68,7 @@ "model": "geogapp.zipcode", "fields" : { "code" : "77002", - "poly" : "SRID=4269;POLYGON ((-95.365015 29.772327, -95.362415 29.772327, -95.360915 29.771827, -95.354615 29.771827, -95.351515 29.772527, -95.350915 29.765327, -95.351015 29.762436, -95.350115 29.760328, -95.347515 29.758528, -95.352315 29.753928, -95.356415 29.756328, -95.358215 29.754028, -95.360215 29.756328, -95.363415 29.757128, -95.364014 29.75638, -95.363415 29.753928, -95.360015 29.751828, -95.361815 29.749528, -95.362715 29.750028, -95.367516 29.744128, -95.369316 29.745128, -95.373916 29.744128, -95.380116 29.738028, -95.387916 29.727929, -95.388516 29.729629, -95.387916 29.732129, -95.382916 29.737428, -95.376616 29.742228, -95.372616 29.747228, -95.378601 29.750846, -95.378616 29.752028, -95.378616 29.754428, -95.376016 29.754528, -95.374616 29.759828, -95.373616 29.761128, -95.371916 29.763928, -95.372316 29.768727, -95.365884 29.76791, -95.366015 29.767127, -95.358715 29.765327, -95.358615 29.766327, -95.359115 29.767227, -95.360215 29.767027, -95.362783 29.768267, -95.365315 29.770527, -95.365015 29.772327))" + "poly" : "POLYGON ((-95.365015 29.772327, -95.362415 29.772327, -95.360915 29.771827, -95.354615 29.771827, -95.351515 29.772527, -95.350915 29.765327, -95.351015 29.762436, -95.350115 29.760328, -95.347515 29.758528, -95.352315 29.753928, -95.356415 29.756328, -95.358215 29.754028, -95.360215 29.756328, -95.363415 29.757128, -95.364014 29.75638, -95.363415 29.753928, -95.360015 29.751828, -95.361815 29.749528, -95.362715 29.750028, -95.367516 29.744128, -95.369316 29.745128, -95.373916 29.744128, -95.380116 29.738028, -95.387916 29.727929, -95.388516 29.729629, -95.387916 29.732129, -95.382916 29.737428, -95.376616 29.742228, -95.372616 29.747228, -95.378601 29.750846, -95.378616 29.752028, -95.378616 29.754428, -95.376016 29.754528, -95.374616 29.759828, -95.373616 29.761128, -95.371916 29.763928, -95.372316 29.768727, -95.365884 29.76791, -95.366015 29.767127, -95.358715 29.765327, -95.358615 29.766327, -95.359115 29.767227, -95.360215 29.767027, -95.362783 29.768267, -95.365315 29.770527, -95.365015 29.772327))" } }, { @@ -76,7 +76,7 @@ "model": "geogapp.zipcode", "fields" : { "code" : "77005", - "poly" : "SRID=4269;POLYGON ((-95.447918 29.727275, -95.428017 29.728729, -95.421117 29.729029, -95.418617 29.727629, -95.418517 29.726429, -95.402117 29.726629, -95.402117 29.725729, -95.395316 29.725729, -95.391916 29.726229, -95.389716 29.725829, -95.396517 29.715429, -95.397517 29.715929, -95.400917 29.711429, -95.411417 29.715029, -95.418417 29.714729, -95.418317 29.70623, -95.440818 29.70593, -95.445018 29.70683, -95.446618 29.70763, -95.447418 29.71003, -95.447918 29.727275))" + "poly" : "POLYGON ((-95.447918 29.727275, -95.428017 29.728729, -95.421117 29.729029, -95.418617 29.727629, -95.418517 29.726429, -95.402117 29.726629, -95.402117 29.725729, -95.395316 29.725729, -95.391916 29.726229, -95.389716 29.725829, -95.396517 29.715429, -95.397517 29.715929, -95.400917 29.711429, -95.411417 29.715029, -95.418417 29.714729, -95.418317 29.70623, -95.440818 29.70593, -95.445018 29.70683, -95.446618 29.70763, -95.447418 29.71003, -95.447918 29.727275))" } }, { @@ -84,7 +84,7 @@ "model": "geogapp.zipcode", "fields" : { "code" : "77025", - "poly" : "SRID=4269;POLYGON ((-95.418317 29.70623, -95.414717 29.706129, -95.414617 29.70533, -95.418217 29.70533, -95.419817 29.69533, -95.419484 29.694196, -95.417166 29.690901, -95.414517 29.69433, -95.413317 29.69263, -95.412617 29.68973, -95.412817 29.68753, -95.414087 29.685055, -95.419165 29.685428, -95.421617 29.68513, -95.425717 29.67983, -95.425017 29.67923, -95.424517 29.67763, -95.427418 29.67763, -95.438018 29.664631, -95.436713 29.664411, -95.440118 29.662231, -95.439218 29.661031, -95.437718 29.660131, -95.435718 29.659731, -95.431818 29.660331, -95.441418 29.656631, -95.441318 29.656331, -95.441818 29.656131, -95.441718 29.659031, -95.441118 29.661031, -95.446718 29.656431, -95.446518 29.673431, -95.446918 29.69013, -95.447418 29.71003, -95.446618 29.70763, -95.445018 29.70683, -95.440818 29.70593, -95.418317 29.70623))" + "poly" : "POLYGON ((-95.418317 29.70623, -95.414717 29.706129, -95.414617 29.70533, -95.418217 29.70533, -95.419817 29.69533, -95.419484 29.694196, -95.417166 29.690901, -95.414517 29.69433, -95.413317 29.69263, -95.412617 29.68973, -95.412817 29.68753, -95.414087 29.685055, -95.419165 29.685428, -95.421617 29.68513, -95.425717 29.67983, -95.425017 29.67923, -95.424517 29.67763, -95.427418 29.67763, -95.438018 29.664631, -95.436713 29.664411, -95.440118 29.662231, -95.439218 29.661031, -95.437718 29.660131, -95.435718 29.659731, -95.431818 29.660331, -95.441418 29.656631, -95.441318 29.656331, -95.441818 29.656131, -95.441718 29.659031, -95.441118 29.661031, -95.446718 29.656431, -95.446518 29.673431, -95.446918 29.69013, -95.447418 29.71003, -95.446618 29.70763, -95.445018 29.70683, -95.440818 29.70593, -95.418317 29.70623))" } }, { @@ -92,7 +92,7 @@ "model": "geogapp.zipcode", "fields" : { "code" : "77401", - "poly" : "SRID=4269;POLYGON ((-95.447918 29.727275, -95.447418 29.71003, -95.446918 29.69013, -95.454318 29.68893, -95.475819 29.68903, -95.475819 29.69113, -95.484419 29.69103, -95.484519 29.69903, -95.480419 29.70133, -95.480419 29.69833, -95.474119 29.69833, -95.474119 29.70453, -95.472719 29.71283, -95.468019 29.71293, -95.468219 29.720229, -95.464018 29.720229, -95.464118 29.724529, -95.463018 29.725929, -95.459818 29.726129, -95.459918 29.720329, -95.451418 29.720429, -95.451775 29.726303, -95.451318 29.727029, -95.447918 29.727275))" + "poly" : "POLYGON ((-95.447918 29.727275, -95.447418 29.71003, -95.446918 29.69013, -95.454318 29.68893, -95.475819 29.68903, -95.475819 29.69113, -95.484419 29.69103, -95.484519 29.69903, -95.480419 29.70133, -95.480419 29.69833, -95.474119 29.69833, -95.474119 29.70453, -95.472719 29.71283, -95.468019 29.71293, -95.468219 29.720229, -95.464018 29.720229, -95.464118 29.724529, -95.463018 29.725929, -95.459818 29.726129, -95.459918 29.720329, -95.451418 29.720429, -95.451775 29.726303, -95.451318 29.727029, -95.447918 29.727275))" } } ] \ No newline at end of file diff --git a/tests/gis_tests/relatedapp/tests.py b/tests/gis_tests/relatedapp/tests.py index 0a385f7625..11a9bd9956 100644 --- a/tests/gis_tests/relatedapp/tests.py +++ b/tests/gis_tests/relatedapp/tests.py @@ -132,7 +132,8 @@ class RelatedGeoModelTest(TestCase): # actually correspond to the centroid of the border. c1 = b1.centroid c2 = c1.transform(2276, clone=True) - Parcel.objects.create(name='P2', city=pcity, center1=c1, center2=c2, border1=b1, border2=b1) + b2 = b1 if connection.features.supports_transform else b1.transform(2276, clone=True) + Parcel.objects.create(name='P2', city=pcity, center1=c1, center2=c2, border1=b1, border2=b2) # Should return the second Parcel, which has the center within the # border. @@ -140,12 +141,15 @@ class RelatedGeoModelTest(TestCase): self.assertEqual(1, len(qs)) self.assertEqual('P2', qs[0].name) + # This time center2 is in a different coordinate system and needs to be + # wrapped in transformation SQL. + qs = Parcel.objects.filter(center2__within=F('border1')) if connection.features.supports_transform: - # This time center2 is in a different coordinate system and needs - # to be wrapped in transformation SQL. - qs = Parcel.objects.filter(center2__within=F('border1')) - self.assertEqual(1, len(qs)) - self.assertEqual('P2', qs[0].name) + self.assertEqual('P2', qs.get().name) + else: + msg = "This backend doesn't support the Transform function." + with self.assertRaisesMessage(NotImplementedError, msg): + list(qs) # Should return the first Parcel, which has the center point equal # to the point in the City ForeignKey. @@ -153,11 +157,14 @@ class RelatedGeoModelTest(TestCase): self.assertEqual(1, len(qs)) self.assertEqual('P1', qs[0].name) + # This time the city column should be wrapped in transformation SQL. + qs = Parcel.objects.filter(border2__contains=F('city__location__point')) if connection.features.supports_transform: - # This time the city column should be wrapped in transformation SQL. - qs = Parcel.objects.filter(border2__contains=F('city__location__point')) - self.assertEqual(1, len(qs)) - self.assertEqual('P1', qs[0].name) + self.assertEqual('P1', qs.get().name) + else: + msg = "This backend doesn't support the Transform function." + with self.assertRaisesMessage(NotImplementedError, msg): + list(qs) def test07_values(self): "Testing values() and values_list()."