Fixed #29116 -- Fixed OpenLayersWidget deserialization ignoring the widget map's SRID.

Regression in 6ecccad711.
This commit is contained in:
Claude Paroz 2018-02-06 11:51:41 +01:00 committed by Tim Graham
parent 09c6d01461
commit 2a2ed0e70a
4 changed files with 34 additions and 9 deletions

View File

@ -35,9 +35,14 @@ class GeometryField(forms.Field):
return None return None
if not isinstance(value, GEOSGeometry): if not isinstance(value, GEOSGeometry):
try: if hasattr(self.widget, 'deserialize'):
value = GEOSGeometry(value) value = self.widget.deserialize(value)
except (GEOSException, ValueError, TypeError): else:
try:
value = GEOSGeometry(value)
except (GEOSException, ValueError, TypeError):
value = None
if value is None:
raise forms.ValidationError(self.error_messages['invalid_geom'], code='invalid_geom') raise forms.ValidationError(self.error_messages['invalid_geom'], code='invalid_geom')
# Try to set the srid # Try to set the srid

View File

@ -2,6 +2,7 @@ import logging
from django.conf import settings from django.conf import settings
from django.contrib.gis import gdal from django.contrib.gis import gdal
from django.contrib.gis.geometry import json_regex
from django.contrib.gis.geos import GEOSException, GEOSGeometry from django.contrib.gis.geos import GEOSException, GEOSGeometry
from django.forms.widgets import Widget from django.forms.widgets import Widget
from django.utils import translation from django.utils import translation
@ -36,7 +37,7 @@ class BaseGeometryWidget(Widget):
def deserialize(self, value): def deserialize(self, value):
try: try:
return GEOSGeometry(value) return GEOSGeometry(value)
except (GEOSException, ValueError) as err: except (GEOSException, ValueError, TypeError) as err:
logger.error("Error creating geometry from value '%s' (%s)", value, err) logger.error("Error creating geometry from value '%s' (%s)", value, err)
return None return None
@ -91,6 +92,13 @@ class OpenLayersWidget(BaseGeometryWidget):
def serialize(self, value): def serialize(self, value):
return value.json if value else '' return value.json if value else ''
def deserialize(self, value):
geom = super().deserialize(value)
# GeoJSON assumes WGS84 (4326). Use the map's SRID instead.
if geom and json_regex.match(value) and self.map_srid != 4326:
geom.srid = self.map_srid
return geom
class OSMWidget(OpenLayersWidget): class OSMWidget(OpenLayersWidget):
""" """

View File

@ -29,3 +29,6 @@ Bugfixes
* Fixed a regression in Django 1.11 where an empty choice could be initially * Fixed a regression in Django 1.11 where an empty choice could be initially
selected for the ``SelectMultiple`` and ``CheckboxSelectMultiple`` widgets selected for the ``SelectMultiple`` and ``CheckboxSelectMultiple`` widgets
(:ticket:`29273`). (:ticket:`29273`).
* Fixed a regression in Django 2.0 where ``OpenLayersWidget`` deserialization
ignored the widget map's SRID and assumed 4326 (WGS84) (:ticket:`29116`).

View File

@ -1,7 +1,7 @@
import re import re
from django.contrib.gis import forms from django.contrib.gis import forms
from django.contrib.gis.forms import BaseGeometryWidget from django.contrib.gis.forms import BaseGeometryWidget, OpenLayersWidget
from django.contrib.gis.geos import GEOSGeometry from django.contrib.gis.geos import GEOSGeometry
from django.forms import ValidationError from django.forms import ValidationError
from django.test import SimpleTestCase, override_settings from django.test import SimpleTestCase, override_settings
@ -31,8 +31,9 @@ class GeometryFieldTest(SimpleTestCase):
fld = forms.GeometryField(srid=32140) fld = forms.GeometryField(srid=32140)
tol = 0.0000001 tol = 0.0000001
xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140) xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140)
# The cleaned geometry should be transformed to 32140. # The cleaned geometry is transformed to 32140 (the widget map_srid is 3857).
cleaned_geom = fld.clean('SRID=4326;POINT (-95.363151 29.763374)') cleaned_geom = fld.clean('SRID=3857;POINT (-10615777.40976205 3473169.895707852)')
self.assertEqual(cleaned_geom.srid, 32140)
self.assertTrue(xform_geom.equals_exact(cleaned_geom, tol)) self.assertTrue(xform_geom.equals_exact(cleaned_geom, tol))
def test_null(self): def test_null(self):
@ -83,6 +84,11 @@ class GeometryFieldTest(SimpleTestCase):
with self.assertRaises(forms.ValidationError): with self.assertRaises(forms.ValidationError):
fld.to_python(wkt) fld.to_python(wkt)
def test_to_python_different_map_srid(self):
f = forms.GeometryField(widget=OpenLayersWidget)
json = '{ "type": "Point", "coordinates": [ 5.0, 23.0 ] }'
self.assertEqual(GEOSGeometry('POINT(5 23)', srid=f.widget.map_srid), f.to_python(json))
def test_field_with_text_widget(self): def test_field_with_text_widget(self):
class PointForm(forms.Form): class PointForm(forms.Form):
pt = forms.PointField(srid=4326, widget=forms.TextInput) pt = forms.PointField(srid=4326, widget=forms.TextInput)
@ -91,6 +97,8 @@ class GeometryFieldTest(SimpleTestCase):
cleaned_pt = form.fields['pt'].clean('POINT(5 23)') cleaned_pt = form.fields['pt'].clean('POINT(5 23)')
self.assertEqual(cleaned_pt, GEOSGeometry('POINT(5 23)', srid=4326)) self.assertEqual(cleaned_pt, GEOSGeometry('POINT(5 23)', srid=4326))
self.assertEqual(4326, cleaned_pt.srid) self.assertEqual(4326, cleaned_pt.srid)
with self.assertRaisesMessage(ValidationError, 'Invalid geometry value.'):
form.fields['pt'].clean('POINT(5)')
point = GEOSGeometry('SRID=4326;POINT(5 23)') point = GEOSGeometry('SRID=4326;POINT(5 23)')
form = PointForm(data={'pt': 'POINT(5 23)'}, initial={'pt': point}) form = PointForm(data={'pt': 'POINT(5 23)'}, initial={'pt': point})
@ -132,8 +140,9 @@ class GeometryFieldTest(SimpleTestCase):
' rows="10" name="pt3"></textarea>', ' rows="10" name="pt3"></textarea>',
output output
) )
# Only the invalid PNT(0) should trigger an error log entry # Only the invalid PNT(0) triggers an error log entry.
self.assertEqual(len(logger_calls), 1) # Deserialization is called in form clean and in widget rendering.
self.assertEqual(len(logger_calls), 2)
self.assertEqual( self.assertEqual(
logger_calls[0], logger_calls[0],
"Error creating geometry from value 'PNT(0)' (String input " "Error creating geometry from value 'PNT(0)' (String input "