Fixed #27964 -- Made MySQL backend raise exception if spatial transformation is needed for query.

This commit is contained in:
Sergey Fedoseev 2017-06-26 16:42:31 +05:00 committed by Tim Graham
parent 964e490847
commit 44a7b98abb
6 changed files with 29 additions and 19 deletions

View File

@ -72,10 +72,7 @@ class BaseSpatialOperations:
backend. backend.
""" """
def transform_value(value, field): def transform_value(value, field):
return ( return value is not None and value.srid != field.srid
not (value is None or value.srid == field.srid) and
self.connection.features.supports_transform
)
if hasattr(value, 'as_sql'): if hasattr(value, 'as_sql'):
return ( return (

View File

@ -610,6 +610,8 @@ of a queryset::
The ``contains`` and ``intersects`` lookups are just a subset of the The ``contains`` and ``intersects`` lookups are just a subset of the
available queries -- the :doc:`db-api` documentation has more. available queries -- the :doc:`db-api` documentation has more.
.. _automatic-spatial-transformations:
Automatic Spatial Transformations Automatic Spatial Transformations
--------------------------------- ---------------------------------
When doing spatial queries, GeoDjango automatically transforms When doing spatial queries, GeoDjango automatically transforms

View File

@ -472,6 +472,10 @@ Miscellaneous
option. Add this to your existing migrations or accept an auto-generated option. Add this to your existing migrations or accept an auto-generated
migration for fields that use it. migration for fields that use it.
* Performing queries that require :ref:`automatic spatial transformations
<automatic-spatial-transformations>` now raises ``NotImplementedError``
on MySQL instead of silently using non-transformed geometries.
.. _deprecated-features-2.0: .. _deprecated-features-2.0:
Features deprecated in 2.0 Features deprecated in 2.0

View File

@ -270,7 +270,7 @@ class GISFunctionsTests(TestCase):
def test_area_with_regular_aggregate(self): def test_area_with_regular_aggregate(self):
# Create projected country objects, for this test to work on all backends. # Create projected country objects, for this test to work on all backends.
for c in Country.objects.all(): 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 # Test in projected coordinate system
qs = CountryWebMercator.objects.annotate(area_sum=Sum(functions.Area('mpoly'))) qs = CountryWebMercator.objects.annotate(area_sum=Sum(functions.Area('mpoly')))
# Some backends (e.g. Oracle) cannot group by multipolygon values, so # Some backends (e.g. Oracle) cannot group by multipolygon values, so

View File

@ -68,7 +68,7 @@
"model": "geogapp.zipcode", "model": "geogapp.zipcode",
"fields" : { "fields" : {
"code" : "77002", "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", "model": "geogapp.zipcode",
"fields" : { "fields" : {
"code" : "77005", "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", "model": "geogapp.zipcode",
"fields" : { "fields" : {
"code" : "77025", "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", "model": "geogapp.zipcode",
"fields" : { "fields" : {
"code" : "77401", "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))"
} }
} }
] ]

View File

@ -132,7 +132,8 @@ class RelatedGeoModelTest(TestCase):
# actually correspond to the centroid of the border. # actually correspond to the centroid of the border.
c1 = b1.centroid c1 = b1.centroid
c2 = c1.transform(2276, clone=True) 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 # Should return the second Parcel, which has the center within the
# border. # border.
@ -140,12 +141,15 @@ class RelatedGeoModelTest(TestCase):
self.assertEqual(1, len(qs)) self.assertEqual(1, len(qs))
self.assertEqual('P2', qs[0].name) 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: if connection.features.supports_transform:
# This time center2 is in a different coordinate system and needs self.assertEqual('P2', qs.get().name)
# to be wrapped in transformation SQL. else:
qs = Parcel.objects.filter(center2__within=F('border1')) msg = "This backend doesn't support the Transform function."
self.assertEqual(1, len(qs)) with self.assertRaisesMessage(NotImplementedError, msg):
self.assertEqual('P2', qs[0].name) list(qs)
# Should return the first Parcel, which has the center point equal # Should return the first Parcel, which has the center point equal
# to the point in the City ForeignKey. # to the point in the City ForeignKey.
@ -153,11 +157,14 @@ class RelatedGeoModelTest(TestCase):
self.assertEqual(1, len(qs)) self.assertEqual(1, len(qs))
self.assertEqual('P1', qs[0].name) 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: if connection.features.supports_transform:
# This time the city column should be wrapped in transformation SQL. self.assertEqual('P1', qs.get().name)
qs = Parcel.objects.filter(border2__contains=F('city__location__point')) else:
self.assertEqual(1, len(qs)) msg = "This backend doesn't support the Transform function."
self.assertEqual('P1', qs[0].name) with self.assertRaisesMessage(NotImplementedError, msg):
list(qs)
def test07_values(self): def test07_values(self):
"Testing values() and values_list()." "Testing values() and values_list()."