Fixed #10660 -- `GeometryField` no longer requires `srid`/`null` keywords, and now respects `required`; coordinate transformations now done inside `gis.forms.GeometryField` -- benefit being that `OSMGeoAdmin` no longer requires 900913 entry in `spatial_ref_sys` thus enabling it to work with MySQL/Oracle spatial backends; added tests for geographic forms.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10634 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2009-04-25 18:24:32 +00:00
parent 8010e0c32e
commit 66db0b43ad
4 changed files with 98 additions and 14 deletions

View File

@ -1 +1,2 @@
from django.forms import *
from django.contrib.gis.forms.fields import GeometryField from django.contrib.gis.forms.fields import GeometryField

View File

@ -1,12 +1,15 @@
from django import forms from django import forms
from django.contrib.gis.db.backend import SpatialBackend
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
# While this couples the geographic forms to the GEOS library,
# it decouples from database (by not importing SpatialBackend).
from django.contrib.gis.geos import GEOSGeometry
class GeometryField(forms.Field): class GeometryField(forms.Field):
""" """
This is the basic form field for a Geometry. Any textual input that is This is the basic form field for a Geometry. Any textual input that is
accepted by SpatialBackend.Geometry is accepted by this form. By default, accepted by GEOSGeometry is accepted by this form. By default,
this is GEOSGeometry, which accepts WKT, HEXEWKB, WKB, and GeoJSON. this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
""" """
widget = forms.Textarea widget = forms.Textarea
@ -14,12 +17,16 @@ class GeometryField(forms.Field):
'no_geom' : _(u'No geometry value provided.'), 'no_geom' : _(u'No geometry value provided.'),
'invalid_geom' : _(u'Invalid geometry value.'), 'invalid_geom' : _(u'Invalid geometry value.'),
'invalid_geom_type' : _(u'Invalid geometry type.'), 'invalid_geom_type' : _(u'Invalid geometry type.'),
} 'transform_error' : _(u'An error occurred when transforming the geometry'
'to the SRID of the geometry form field.'),
}
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.null = kwargs.pop('null') # Pop out attributes from the database field, or use sensible
self.geom_type = kwargs.pop('geom_type') # defaults (e.g., allow None).
self.srid = kwargs.pop('srid') self.srid = kwargs.pop('srid', None)
self.geom_type = kwargs.pop('geom_type', 'GEOMETRY')
self.null = kwargs.pop('null', True)
super(GeometryField, self).__init__(**kwargs) super(GeometryField, self).__init__(**kwargs)
def clean(self, value): def clean(self, value):
@ -29,15 +36,15 @@ class GeometryField(forms.Field):
the value cannot be instantiated as a Geometry. the value cannot be instantiated as a Geometry.
""" """
if not value: if not value:
if self.null: if self.null and not self.required:
# The geometry column allows NULL, return None. # The geometry column allows NULL and is not required.
return None return None
else: else:
raise forms.ValidationError(self.error_messages['no_geom']) raise forms.ValidationError(self.error_messages['no_geom'])
# Trying to create a Geometry object from the form value.
try: try:
# Trying to create a Geometry object from the form value. geom = GEOSGeometry(value)
geom = SpatialBackend.Geometry(value)
except: except:
raise forms.ValidationError(self.error_messages['invalid_geom']) raise forms.ValidationError(self.error_messages['invalid_geom'])
@ -46,4 +53,15 @@ class GeometryField(forms.Field):
if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY': if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
raise forms.ValidationError(self.error_messages['invalid_geom_type']) raise forms.ValidationError(self.error_messages['invalid_geom_type'])
# Transforming the geometry if the SRID was set.
if self.srid:
if not geom.srid:
# Should match that of the field if not given.
geom.srid = self.srid
elif self.srid != -1 and self.srid != geom.srid:
try:
geom.transform(self.srid)
except:
raise forms.ValidationError(self.error_messages['transform_error'])
return geom return geom

View File

@ -35,7 +35,7 @@ def geo_suite():
if HAS_GDAL: if HAS_GDAL:
# These tests require GDAL. # These tests require GDAL.
test_suite_names.append('test_spatialrefsys') test_suite_names.extend(['test_spatialrefsys', 'test_geoforms'])
test_apps.append('layermap') test_apps.append('layermap')
# Adding the GDAL tests. # Adding the GDAL tests.

View File

@ -0,0 +1,65 @@
import unittest
from django.forms import ValidationError
from django.contrib.gis import forms
from django.contrib.gis.geos import GEOSGeometry
class GeometryFieldTest(unittest.TestCase):
def test00_init(self):
"Testing GeometryField initialization with defaults."
fld = forms.GeometryField()
for bad_default in ('blah', 3, 'FoO', None, 0):
self.assertRaises(ValidationError, fld.clean, bad_default)
def test01_srid(self):
"Testing GeometryField with a SRID set."
# Input that doesn't specify the SRID is assumed to be in the SRID
# of the input field.
fld = forms.GeometryField(srid=4326)
geom = fld.clean('POINT(5 23)')
self.assertEqual(4326, geom.srid)
# Making the field in a different SRID from that of the geometry, and
# asserting it transforms.
fld = forms.GeometryField(srid=32140)
tol = 0.0000001
xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140)
# The cleaned geometry should be transformed to 32140.
cleaned_geom = fld.clean('SRID=4326;POINT (-95.363151 29.763374)')
self.failUnless(xform_geom.equals_exact(cleaned_geom, tol))
def test02_null(self):
"Testing GeometryField's handling of null (None) geometries."
# Form fields, by default, are required (`required=True`)
fld = forms.GeometryField()
self.assertRaises(forms.ValidationError, fld.clean, None)
# Still not allowed if `null=False`.
fld = forms.GeometryField(required=False, null=False)
self.assertRaises(forms.ValidationError, fld.clean, None)
# This will clean None as a geometry (See #10660).
fld = forms.GeometryField(required=False)
self.assertEqual(None, fld.clean(None))
def test03_geom_type(self):
"Testing GeometryField's handling of different geometry types."
# By default, all geometry types are allowed.
fld = forms.GeometryField()
for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'):
self.assertEqual(GEOSGeometry(wkt), fld.clean(wkt))
pnt_fld = forms.GeometryField(geom_type='POINT')
self.assertEqual(GEOSGeometry('POINT(5 23)'), pnt_fld.clean('POINT(5 23)'))
self.assertRaises(forms.ValidationError, pnt_fld.clean, 'LINESTRING(0 0, 1 1)')
def suite():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(GeometryFieldTest))
return s
def run(verbosity=2):
unittest.TextTestRunner(verbosity=verbosity).run(suite())
if __name__=="__main__":
run()