Fixed #27056 -- Allowed migrating geometry field dimension on PostGIS

Thanks Tim Graham for the review.
This commit is contained in:
Claude Paroz 2016-09-19 09:58:58 +02:00
parent 8e2ac3bcaf
commit 92323d54fd
3 changed files with 55 additions and 2 deletions

View File

@ -6,6 +6,9 @@ class PostGISSchemaEditor(DatabaseSchemaEditor):
geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND' geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND'
rast_index_wrapper = 'ST_ConvexHull(%s)' rast_index_wrapper = 'ST_ConvexHull(%s)'
sql_alter_column_to_3d = "ALTER COLUMN %(column)s TYPE %(type)s USING ST_Force3D(%(column)s)::%(type)s"
sql_alter_column_to_2d = "ALTER COLUMN %(column)s TYPE %(type)s USING ST_Force2D(%(column)s)::%(type)s"
def geo_quote_name(self, name): def geo_quote_name(self, name):
return self.connection.ops.geo_quote_name(name) return self.connection.ops.geo_quote_name(name)
@ -36,3 +39,29 @@ class PostGISSchemaEditor(DatabaseSchemaEditor):
"columns": field_column, "columns": field_column,
"extra": '', "extra": '',
} }
def _alter_column_type_sql(self, table, old_field, new_field, new_type):
"""
Special case when dimension changed.
"""
if not hasattr(old_field, 'dim') or not hasattr(new_field, 'dim'):
return super(PostGISSchemaEditor, self)._alter_column_type_sql(
table, old_field, new_field, new_type
)
if old_field.dim == 2 and new_field.dim == 3:
sql_alter = self.sql_alter_column_to_3d
elif old_field.dim == 3 and new_field.dim == 2:
sql_alter = self.sql_alter_column_to_2d
else:
sql_alter = self.sql_alter_column_type
return (
(
sql_alter % {
"column": self.quote_name(new_field.column),
"type": new_type,
},
[],
),
[],
)

View File

@ -141,6 +141,8 @@ Minor features
``https://cdnjs.cloudflare.com`` which is more suitable for production use ``https://cdnjs.cloudflare.com`` which is more suitable for production use
than the the old ``http://openlayers.org`` source. than the the old ``http://openlayers.org`` source.
* PostGIS migrations can now change field dimensions.
:mod:`django.contrib.messages` :mod:`django.contrib.messages`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,6 +1,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from unittest import skipIf
from django.contrib.gis.db.models import fields from django.contrib.gis.db.models import fields
from django.contrib.gis.geos import MultiPolygon, Polygon
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.db import connection, migrations, models from django.db import connection, migrations, models
from django.db.migrations.migration import Migration from django.db.migrations.migration import Migration
@ -9,7 +12,7 @@ from django.test import (
TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature,
) )
from ..utils import mysql from ..utils import mysql, spatialite
if connection.features.gis_enabled: if connection.features.gis_enabled:
try: try:
@ -59,7 +62,7 @@ class OperationTestCase(TransactionTestCase):
('geom', fields.MultiPolygonField(srid=4326)) ('geom', fields.MultiPolygonField(srid=4326))
] ]
if connection.features.supports_raster or force_raster_creation: if connection.features.supports_raster or force_raster_creation:
test_fields += [('rast', fields.RasterField(srid=4326))] test_fields += [('rast', fields.RasterField(srid=4326, null=True))]
operations = [migrations.CreateModel('Neighborhood', test_fields)] operations = [migrations.CreateModel('Neighborhood', test_fields)]
self.current_state = self.apply_operations('gis', ProjectState(), operations) self.current_state = self.apply_operations('gis', ProjectState(), operations)
@ -187,6 +190,25 @@ class OperationTests(OperationTestCase):
if connection.features.supports_raster: if connection.features.supports_raster:
self.assertSpatialIndexExists('gis_neighborhood', 'rast', raster=True) self.assertSpatialIndexExists('gis_neighborhood', 'rast', raster=True)
@skipUnlessDBFeature("supports_3d_storage")
@skipIf(spatialite, "Django currently doesn't support altering Spatialite geometry fields")
def test_alter_geom_field_dim(self):
Neighborhood = self.current_state.apps.get_model('gis', 'Neighborhood')
p1 = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
Neighborhood.objects.create(name='TestDim', geom=MultiPolygon(p1, p1))
# Add 3rd dimension.
self.alter_gis_model(
migrations.AlterField, 'Neighborhood', 'geom', False,
fields.MultiPolygonField, field_class_kwargs={'srid': 4326, 'dim': 3}
)
self.assertTrue(Neighborhood.objects.first().geom.hasz)
# Rewind to 2 dimensions.
self.alter_gis_model(
migrations.AlterField, 'Neighborhood', 'geom', False,
fields.MultiPolygonField, field_class_kwargs={'srid': 4326, 'dim': 2}
)
self.assertFalse(Neighborhood.objects.first().geom.hasz)
@skipIfDBFeature('supports_raster') @skipIfDBFeature('supports_raster')
class NoRasterSupportTests(OperationTestCase): class NoRasterSupportTests(OperationTestCase):