Fixed #23537 -- Added Oracle GIS SchemaEditor.
Thanks Shai Berger for review.
This commit is contained in:
parent
45bd7b3bd9
commit
a8f07530a7
|
@ -6,6 +6,7 @@ from django.contrib.gis.db.backends.base import BaseSpatialFeatures
|
|||
from django.contrib.gis.db.backends.oracle.creation import OracleCreation
|
||||
from django.contrib.gis.db.backends.oracle.introspection import OracleIntrospection
|
||||
from django.contrib.gis.db.backends.oracle.operations import OracleOperations
|
||||
from django.contrib.gis.db.backends.oracle.schema import OracleGISSchemaEditor
|
||||
|
||||
|
||||
class DatabaseFeatures(BaseSpatialFeatures, OracleDatabaseFeatures):
|
||||
|
@ -20,3 +21,6 @@ class DatabaseWrapper(OracleDatabaseWrapper):
|
|||
self.ops = OracleOperations(self)
|
||||
self.creation = OracleCreation(self)
|
||||
self.introspection = OracleIntrospection(self)
|
||||
|
||||
def schema_editor(self, *args, **kwargs):
|
||||
return OracleGISSchemaEditor(self, *args, **kwargs)
|
||||
|
|
|
@ -142,6 +142,9 @@ class OracleOperations(DatabaseOperations, BaseSpatialOperations):
|
|||
|
||||
truncate_params = {'relate': None}
|
||||
|
||||
def geo_quote_name(self, name):
|
||||
return super(OracleOperations, self).geo_quote_name(name).upper()
|
||||
|
||||
def get_db_converters(self, internal_type):
|
||||
converters = super(OracleOperations, self).get_db_converters(internal_type)
|
||||
geometry_fields = (
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
from django.contrib.gis.db.models.fields import GeometryField
|
||||
from django.db.backends.oracle.schema import DatabaseSchemaEditor
|
||||
from django.db.backends.utils import truncate_name
|
||||
|
||||
|
||||
class OracleGISSchemaEditor(DatabaseSchemaEditor):
|
||||
sql_add_geometry_metadata = ("""
|
||||
INSERT INTO USER_SDO_GEOM_METADATA
|
||||
("TABLE_NAME", "COLUMN_NAME", "DIMINFO", "SRID")
|
||||
VALUES (
|
||||
%(table)s,
|
||||
%(column)s,
|
||||
MDSYS.SDO_DIM_ARRAY(
|
||||
MDSYS.SDO_DIM_ELEMENT('LONG', %(dim0)s, %(dim2)s, %(tolerance)s),
|
||||
MDSYS.SDO_DIM_ELEMENT('LAT', %(dim1)s, %(dim3)s, %(tolerance)s)
|
||||
),
|
||||
%(srid)s
|
||||
)""")
|
||||
sql_add_spatial_index = 'CREATE INDEX %(index)s ON %(table)s(%(column)s) INDEXTYPE IS MDSYS.SPATIAL_INDEX'
|
||||
sql_drop_spatial_index = 'DROP INDEX %(index)s'
|
||||
sql_clear_geometry_table_metadata = 'DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s'
|
||||
sql_clear_geometry_field_metadata = (
|
||||
'DELETE FROM USER_SDO_GEOM_METADATA WHERE TABLE_NAME = %(table)s '
|
||||
'AND COLUMN_NAME = %(column)s'
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OracleGISSchemaEditor, self).__init__(*args, **kwargs)
|
||||
self.geometry_sql = []
|
||||
|
||||
def geo_quote_name(self, name):
|
||||
return self.connection.ops.geo_quote_name(name)
|
||||
|
||||
def column_sql(self, model, field, include_default=False):
|
||||
column_sql = super(OracleGISSchemaEditor, self).column_sql(model, field, include_default)
|
||||
if isinstance(field, GeometryField):
|
||||
db_table = model._meta.db_table
|
||||
self.geometry_sql.append(
|
||||
self.sql_add_geometry_metadata % {
|
||||
'table': self.geo_quote_name(db_table),
|
||||
'column': self.geo_quote_name(field.column),
|
||||
'dim0': field._extent[0],
|
||||
'dim1': field._extent[1],
|
||||
'dim2': field._extent[2],
|
||||
'dim3': field._extent[3],
|
||||
'tolerance': field._tolerance,
|
||||
'srid': field.srid,
|
||||
}
|
||||
)
|
||||
if field.spatial_index:
|
||||
self.geometry_sql.append(
|
||||
self.sql_add_spatial_index % {
|
||||
'index': self.quote_name(self._create_spatial_index_name(model, field)),
|
||||
'table': self.quote_name(db_table),
|
||||
'column': self.quote_name(field.column),
|
||||
}
|
||||
)
|
||||
return column_sql
|
||||
|
||||
def create_model(self, model):
|
||||
super(OracleGISSchemaEditor, self).create_model(model)
|
||||
self.run_geometry_sql()
|
||||
|
||||
def delete_model(self, model):
|
||||
super(OracleGISSchemaEditor, self).delete_model(model)
|
||||
self.execute(self.sql_clear_geometry_table_metadata % {
|
||||
'table': self.geo_quote_name(model._meta.db_table),
|
||||
})
|
||||
|
||||
def add_field(self, model, field):
|
||||
super(OracleGISSchemaEditor, self).add_field(model, field)
|
||||
self.run_geometry_sql()
|
||||
|
||||
def remove_field(self, model, field):
|
||||
if isinstance(field, GeometryField):
|
||||
self.execute(self.sql_clear_geometry_field_metadata % {
|
||||
'table': self.geo_quote_name(model._meta.db_table),
|
||||
'column': self.geo_quote_name(field.column),
|
||||
})
|
||||
if field.spatial_index:
|
||||
self.execute(self.sql_drop_spatial_index % {
|
||||
'index': self.quote_name(self._create_spatial_index_name(model, field)),
|
||||
})
|
||||
super(OracleGISSchemaEditor, self).remove_field(model, field)
|
||||
|
||||
def run_geometry_sql(self):
|
||||
for sql in self.geometry_sql:
|
||||
self.execute(sql)
|
||||
self.geometry_sql = []
|
||||
|
||||
def _create_spatial_index_name(self, model, field):
|
||||
# Oracle doesn't allow object names > 30 characters. Use this scheme
|
||||
# instead of self._create_index_name() for backwards compatibility.
|
||||
return truncate_name('%s_%s_id' % (model._meta.db_table, field.column), 30)
|
|
@ -51,6 +51,17 @@ class OperationTests(TransactionTestCase):
|
|||
)]
|
||||
return self.apply_operations('gis', ProjectState(), operations)
|
||||
|
||||
def assertGeometryColumnsCount(self, expected_count):
|
||||
table_name = "gis_neighborhood"
|
||||
if connection.features.uppercases_column_names:
|
||||
table_name = table_name.upper()
|
||||
self.assertEqual(
|
||||
GeometryColumns.objects.filter(**{
|
||||
GeometryColumns.table_name_col(): table_name,
|
||||
}).count(),
|
||||
expected_count
|
||||
)
|
||||
|
||||
def test_add_gis_field(self):
|
||||
"""
|
||||
Tests the AddField operation with a GIS-enabled column.
|
||||
|
@ -70,10 +81,7 @@ class OperationTests(TransactionTestCase):
|
|||
|
||||
# Test GeometryColumns when available
|
||||
if HAS_GEOMETRY_COLUMNS:
|
||||
self.assertEqual(
|
||||
GeometryColumns.objects.filter(**{GeometryColumns.table_name_col(): "gis_neighborhood"}).count(),
|
||||
2
|
||||
)
|
||||
self.assertGeometryColumnsCount(2)
|
||||
|
||||
if self.has_spatial_indexes:
|
||||
with connection.cursor() as cursor:
|
||||
|
@ -95,10 +103,7 @@ class OperationTests(TransactionTestCase):
|
|||
|
||||
# Test GeometryColumns when available
|
||||
if HAS_GEOMETRY_COLUMNS:
|
||||
self.assertEqual(
|
||||
GeometryColumns.objects.filter(**{GeometryColumns.table_name_col(): "gis_neighborhood"}).count(),
|
||||
0
|
||||
)
|
||||
self.assertGeometryColumnsCount(0)
|
||||
|
||||
def test_create_model_spatial_index(self):
|
||||
self.current_state = self.set_up_test_model()
|
||||
|
|
|
@ -77,6 +77,9 @@ Bugfixes
|
|||
* Added ``SchemaEditor`` for MySQL GIS backend so that spatial indexes will be
|
||||
created for apps with migrations (:ticket:`23538`).
|
||||
|
||||
* Added ``SchemaEditor`` for Oracle GIS backend so that spatial metadata and
|
||||
indexes will be created for apps with migrations (:ticket:`23537`).
|
||||
|
||||
* Coerced the ``related_name`` model field option to unicode during migration
|
||||
generation to generate migrations that work with both Python 2 and 3
|
||||
(:ticket:`23455`).
|
||||
|
|
Loading…
Reference in New Issue