From 183f5015408ffe0d3a22203ddaf7a3a56da025fd Mon Sep 17 00:00:00 2001 From: Sergey Fedoseev Date: Fri, 17 Jun 2016 20:03:50 +0500 Subject: [PATCH] Fixed #26789 -- Fixed handling of empty geometries in BaseSpatialField.get_db_prep_save(). --- .../contrib/gis/db/backends/base/features.py | 2 ++ .../gis/db/backends/postgis/features.py | 1 + django/contrib/gis/db/models/fields.py | 6 ++--- tests/gis_tests/geoapp/tests.py | 27 +++++++++++++++++-- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/django/contrib/gis/db/backends/base/features.py b/django/contrib/gis/db/backends/base/features.py index 451b515625..3d1dfba1dd 100644 --- a/django/contrib/gis/db/backends/base/features.py +++ b/django/contrib/gis/db/backends/base/features.py @@ -26,6 +26,8 @@ class BaseSpatialFeatures(object): supports_real_shape_operations = True # Can geometry fields be null? supports_null_geometries = True + # Are empty geometries supported? + supports_empty_geometries = False # Can the the function be applied on geodetic coordinate systems? supports_distance_geodetic = True supports_length_geodetic = True diff --git a/django/contrib/gis/db/backends/postgis/features.py b/django/contrib/gis/db/backends/postgis/features.py index ea1d450008..2d613efe6e 100644 --- a/django/contrib/gis/db/backends/postgis/features.py +++ b/django/contrib/gis/db/backends/postgis/features.py @@ -8,3 +8,4 @@ class DatabaseFeatures(BaseSpatialFeatures, Psycopg2DatabaseFeatures): supports_3d_functions = True supports_left_right_lookups = True supports_raster = True + supports_empty_geometries = True diff --git a/django/contrib/gis/db/models/fields.py b/django/contrib/gis/db/models/fields.py index a3813edd58..7519b30fb2 100644 --- a/django/contrib/gis/db/models/fields.py +++ b/django/contrib/gis/db/models/fields.py @@ -177,10 +177,10 @@ class BaseSpatialField(Field): """ Prepare the value for saving in the database. """ - if not value: - return None - else: + if isinstance(value, Geometry) or value: return connection.ops.Adapter(self.get_prep_value(value)) + else: + return None def get_raster_prep_value(self, value, is_candidate): """ diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py index aede5950cd..9d03d6ff78 100644 --- a/tests/gis_tests/geoapp/tests.py +++ b/tests/gis_tests/geoapp/tests.py @@ -6,8 +6,8 @@ import tempfile from django.contrib.gis import gdal from django.contrib.gis.db.models import Extent, MakeLine, Union from django.contrib.gis.geos import ( - GeometryCollection, GEOSGeometry, LinearRing, LineString, Point, Polygon, - fromstr, + GeometryCollection, GEOSGeometry, LinearRing, LineString, MultiLineString, + MultiPoint, MultiPolygon, Point, Polygon, fromstr, ) from django.core.management import call_command from django.db import connection @@ -215,6 +215,29 @@ class GeoModelTest(TestCase): call_command('loaddata', tmp.name, verbosity=0) self.assertListEqual(original_data, list(City.objects.all().order_by('name'))) + @skipUnlessDBFeature("supports_empty_geometries") + def test_empty_geometries(self): + geometry_classes = [ + Point, + LineString, + LinearRing, + Polygon, + MultiPoint, + MultiLineString, + MultiPolygon, + GeometryCollection, + ] + for klass in geometry_classes: + g = klass(srid=4326) + feature = Feature.objects.create(name='Empty %s' % klass.__name__, geom=g) + feature.refresh_from_db() + if klass is LinearRing: + # LinearRing isn't representable in WKB, so GEOSGeomtry.wkb + # uses LineString instead. + g = LineString(srid=4326) + self.assertEqual(feature.geom, g) + self.assertEqual(feature.geom.srid, g.srid) + @skipUnlessDBFeature("gis_enabled") class GeoLookupTest(TestCase):