From 44c062344f77708fd68189cbd80792daf524e5a6 Mon Sep 17 00:00:00 2001 From: Justin Bronn Date: Fri, 24 Apr 2009 20:24:50 +0000 Subject: [PATCH] Fixed #10888 -- May now insert NULL `GeometryField` values on Oracle. git-svn-id: http://code.djangoproject.com/svn/django/trunk@10631 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/gis/db/backend/oracle/field.py | 2 +- django/contrib/gis/db/models/manager.py | 4 ++ .../contrib/gis/db/models/sql/subqueries.py | 39 +++++++++++++++++++ django/contrib/gis/tests/geoapp/tests.py | 25 +++++------- 4 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 django/contrib/gis/db/models/sql/subqueries.py diff --git a/django/contrib/gis/db/backend/oracle/field.py b/django/contrib/gis/db/backend/oracle/field.py index 4835f44a12..d6e3640617 100644 --- a/django/contrib/gis/db/backend/oracle/field.py +++ b/django/contrib/gis/db/backend/oracle/field.py @@ -94,7 +94,7 @@ class OracleSpatialField(Field): SDO_CS.TRANSFORM() function call. """ if value is None: - return '%s' + return 'NULL' elif value.srid != self.srid: # Adding Transform() to the SQL placeholder. return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self.srid) diff --git a/django/contrib/gis/db/models/manager.py b/django/contrib/gis/db/models/manager.py index b4cc14b5b6..2bfda990ff 100644 --- a/django/contrib/gis/db/models/manager.py +++ b/django/contrib/gis/db/models/manager.py @@ -1,5 +1,6 @@ from django.db.models.manager import Manager from django.contrib.gis.db.models.query import GeoQuerySet +from django.contrib.gis.db.models.sql.subqueries import insert_query class GeoManager(Manager): "Overrides Manager to return Geographic QuerySets." @@ -86,3 +87,6 @@ class GeoManager(Manager): def unionagg(self, *args, **kwargs): return self.get_query_set().unionagg(*args, **kwargs) + + def _insert(self, values, **kwargs): + return insert_query(self.model, values, **kwargs) diff --git a/django/contrib/gis/db/models/sql/subqueries.py b/django/contrib/gis/db/models/sql/subqueries.py new file mode 100644 index 0000000000..ed58cc5687 --- /dev/null +++ b/django/contrib/gis/db/models/sql/subqueries.py @@ -0,0 +1,39 @@ +from django.contrib.gis.db.backend import SpatialBackend +from django.db.models.query import insert_query + +if SpatialBackend.oracle: + from django.db import connection + from django.db.models.sql.subqueries import InsertQuery + + class OracleGeoInsertQuery(InsertQuery): + def insert_values(self, insert_values, raw_values=False): + """ + This routine is overloaded from InsertQuery so that no parameter is + passed into cx_Oracle for NULL geometries. The reason is that + cx_Oracle has no way to bind Oracle object values (like + MDSYS.SDO_GEOMETRY). + """ + placeholders, values = [], [] + for field, val in insert_values: + if hasattr(field, 'get_placeholder'): + ph = field.get_placeholder(val) + else: + ph = '%s' + + placeholders.append(ph) + self.columns.append(field.column) + + # If 'NULL' for the placeholder, omit appending None + # to the values list (which is used for db params). + if not ph == 'NULL': + values.append(val) + if raw_values: + self.values.extend(values) + else: + self.params += tuple(values) + self.values.extend(placeholders) + + def insert_query(model, values, return_id=False, raw_values=False): + query = OracleGeoInsertQuery(model, connection) + query.insert_values(values, raw_values) + return query.execute_sql(return_id) diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py index f756f6222c..1eaed196b0 100644 --- a/django/contrib/gis/tests/geoapp/tests.py +++ b/django/contrib/gis/tests/geoapp/tests.py @@ -30,7 +30,7 @@ class GeoModelTest(unittest.TestCase): data_dir = os.path.join(os.path.dirname(__file__), 'sql') def get_file(wkt_file): return os.path.join(data_dir, wkt_file) - + State(name='Puerto Rico', poly=None).save() State(name='Colorado', poly=fromfile(get_file('co.wkt'))).save() State(name='Kansas', poly=fromfile(get_file('ks.wkt'))).save() Country(name='Texas', mpoly=fromfile(get_file('tx.wkt'))).save() @@ -39,13 +39,7 @@ class GeoModelTest(unittest.TestCase): # Ensuring that data was loaded from initial SQL. self.assertEqual(2, Country.objects.count()) self.assertEqual(8, City.objects.count()) - - # Only PostGIS can handle NULL geometries - if SpatialBackend.postgis or SpatialBackend.spatialite: - n_state = 3 - else: - n_state = 2 - self.assertEqual(n_state, State.objects.count()) + self.assertEqual(3, State.objects.count()) def test02_proxy(self): "Testing Lazy-Geometry support (using the GeometryProxy)." @@ -369,10 +363,6 @@ class GeoModelTest(unittest.TestCase): m1.save() self.assertEqual(-1, m1.geom.srid) - # Oracle does not support NULL geometries in its spatial index for - # some routines (e.g., SDO_GEOM.RELATE). - @no_oracle - @no_spatialite def test12_null_geometries(self): "Testing NULL geometry support, and the `isnull` lookup type." if DISABLE: return @@ -391,9 +381,14 @@ class GeoModelTest(unittest.TestCase): self.assertEqual(True, 'Kansas' in state_names) # Saving another commonwealth w/a NULL geometry. - if not SpatialBackend.oracle: - # TODO: Fix saving w/NULL geometry on Oracle. - State(name='Northern Mariana Islands', poly=None).save() + nmi = State.objects.create(name='Northern Mariana Islands', poly=None) + self.assertEqual(nmi.poly, None) + + # Assigning a geomery and saving -- then UPDATE back to NULL. + nmi.poly = 'POLYGON((0 0,1 0,1 1,1 0,0 0))' + nmi.save() + State.objects.filter(name='Northern Mariana Islands').update(poly=None) + self.assertEqual(None, State.objects.get(name='Northern Mariana Islands').poly) @no_oracle # No specific `left` or `right` operators in Oracle. @no_spatialite # No `left` or `right` operators in SpatiaLite.