Use native geometry types on PostGIS 2.0+ instead of `AddGeometryColumn` and don't query database in `PostGISCreation.sql_table_creation_suffix`.

This commit is contained in:
Justin Bronn 2012-10-06 09:57:24 -07:00
parent b6b8a3f66b
commit 91ef2a5253
4 changed files with 33 additions and 27 deletions

View File

@ -32,8 +32,9 @@ class BaseSpatialOperations(object):
# How the geometry column should be selected. # How the geometry column should be selected.
select = None select = None
# Does the spatial database have a geography type? # Does the spatial database have a geometry or geography type?
geography = False geography = False
geometry = False
area = False area = False
centroid = False centroid = False

View File

@ -4,7 +4,8 @@ from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation
class PostGISCreation(DatabaseCreation): class PostGISCreation(DatabaseCreation):
geom_index_type = 'GIST' geom_index_type = 'GIST'
geom_index_opts = 'GIST_GEOMETRY_OPS' geom_index_ops = 'GIST_GEOMETRY_OPS'
geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND'
def sql_indexes_for_field(self, model, f, style): def sql_indexes_for_field(self, model, f, style):
"Return any spatial index creation SQL for the field." "Return any spatial index creation SQL for the field."
@ -17,8 +18,9 @@ class PostGISCreation(DatabaseCreation):
qn = self.connection.ops.quote_name qn = self.connection.ops.quote_name
db_table = model._meta.db_table db_table = model._meta.db_table
if f.geography: if f.geography or self.connection.ops.geometry:
# Geogrophy columns are created normally. # Geography and Geometry (PostGIS 2.0+) columns are
# created normally.
pass pass
else: else:
# Geometry columns are created by `AddGeometryColumn` # Geometry columns are created by `AddGeometryColumn`
@ -47,33 +49,23 @@ class PostGISCreation(DatabaseCreation):
# which are fast on multidimensional cases, or just plain # which are fast on multidimensional cases, or just plain
# gist index for the 2d case. # gist index for the 2d case.
if f.geography: if f.geography:
index_opts = '' index_ops = ''
elif self.connection.ops.spatial_version >= (2, 0): elif self.connection.ops.geometry:
if f.dim > 2: if f.dim > 2:
index_opts = ' ' + style.SQL_KEYWORD('gist_geometry_ops_nd') index_ops = ' ' + style.SQL_KEYWORD(self.geom_index_ops_nd)
else: else:
index_opts = '' index_ops = ''
else: else:
index_opts = ' ' + style.SQL_KEYWORD(self.geom_index_opts) index_ops = ' ' + style.SQL_KEYWORD(self.geom_index_ops)
output.append(style.SQL_KEYWORD('CREATE INDEX ') + output.append(style.SQL_KEYWORD('CREATE INDEX ') +
style.SQL_TABLE(qn('%s_%s_id' % (db_table, f.column))) + style.SQL_TABLE(qn('%s_%s_id' % (db_table, f.column))) +
style.SQL_KEYWORD(' ON ') + style.SQL_KEYWORD(' ON ') +
style.SQL_TABLE(qn(db_table)) + style.SQL_TABLE(qn(db_table)) +
style.SQL_KEYWORD(' USING ') + style.SQL_KEYWORD(' USING ') +
style.SQL_COLTYPE(self.geom_index_type) + ' ( ' + style.SQL_COLTYPE(self.geom_index_type) + ' ( ' +
style.SQL_FIELD(qn(f.column)) + index_opts + ' );') style.SQL_FIELD(qn(f.column)) + index_ops + ' );')
return output return output
def sql_table_creation_suffix(self): def sql_table_creation_suffix(self):
cursor = self.connection.cursor()
cursor.execute('SELECT datname FROM pg_database;')
db_names = [row[0] for row in cursor.fetchall()]
postgis_template = getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis') postgis_template = getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis')
return ' TEMPLATE %s' % self.connection.ops.quote_name(postgis_template)
if postgis_template in db_names:
qn = self.connection.ops.quote_name
return ' TEMPLATE %s' % qn(postgis_template)
elif self.connection.ops.spatial_version < (2, 0):
raise ImproperlyConfigured("Template database '%s' does not exist." % postgis_template)
else:
return ''

View File

@ -103,11 +103,12 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
self.geom_func_prefix = prefix self.geom_func_prefix = prefix
self.spatial_version = version self.spatial_version = version
except DatabaseError: except DatabaseError:
raise ImproperlyConfigured('Cannot determine PostGIS version for database "%s". ' raise ImproperlyConfigured(
'GeoDjango requires at least PostGIS version 1.3. ' 'Cannot determine PostGIS version for database "%s". '
'Was the database created from a spatial database ' 'GeoDjango requires at least PostGIS version 1.3. '
'template?' % self.connection.settings_dict['NAME'] 'Was the database created from a spatial database '
) 'template?' % self.connection.settings_dict['NAME']
)
# TODO: Raise helpful exceptions as they become known. # TODO: Raise helpful exceptions as they become known.
# PostGIS-specific operators. The commented descriptions of these # PostGIS-specific operators. The commented descriptions of these
@ -215,6 +216,10 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
'bboverlaps' : PostGISOperator('&&'), 'bboverlaps' : PostGISOperator('&&'),
} }
# Native geometry type support added in PostGIS 2.0.
if version >= (2, 0, 0):
self.geometry = True
# Creating a dictionary lookup of all GIS terms for PostGIS. # Creating a dictionary lookup of all GIS terms for PostGIS.
gis_terms = ['isnull'] gis_terms = ['isnull']
gis_terms += list(self.geometry_operators) gis_terms += list(self.geometry_operators)
@ -320,6 +325,14 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
'only with an SRID of 4326.') 'only with an SRID of 4326.')
return 'geography(%s,%d)'% (f.geom_type, f.srid) return 'geography(%s,%d)'% (f.geom_type, f.srid)
elif self.geometry:
# Postgis 2.0 supports type-based geometries.
# TODO: Support 'M' extension.
if f.dim == 3:
geom_type = f.geom_type + 'Z'
else:
geom_type = f.geom_type
return 'geometry(%s,%d)' % (geom_type, f.srid)
else: else:
return None return None

View File

@ -95,7 +95,7 @@ class GeometryField(Field):
# Is this a geography rather than a geometry column? # Is this a geography rather than a geometry column?
self.geography = geography self.geography = geography
# Oracle-specific private attributes for creating the entrie in # Oracle-specific private attributes for creating the entry in
# `USER_SDO_GEOM_METADATA` # `USER_SDO_GEOM_METADATA`
self._extent = kwargs.pop('extent', (-180.0, -90.0, 180.0, 90.0)) self._extent = kwargs.pop('extent', (-180.0, -90.0, 180.0, 90.0))
self._tolerance = kwargs.pop('tolerance', 0.05) self._tolerance = kwargs.pop('tolerance', 0.05)