Loading django/contrib/gis/db/backends/postgis/base.py +3 −17 Original line number Diff line number Diff line from django.conf import settings from django.db.backends.base.base import NO_DB_ALIAS from django.db.backends.postgresql_psycopg2.base import \ DatabaseWrapper as Psycopg2DatabaseWrapper from django.utils.functional import cached_property from .creation import PostGISCreation from .features import DatabaseFeatures from .introspection import PostGISIntrospection from .operations import PostGISOperations Loading @@ -18,22 +15,11 @@ class DatabaseWrapper(Psycopg2DatabaseWrapper): super(DatabaseWrapper, self).__init__(*args, **kwargs) if kwargs.get('alias', '') != NO_DB_ALIAS: self.features = DatabaseFeatures(self) self.creation = PostGISCreation(self) self.ops = PostGISOperations(self) self.introspection = PostGISIntrospection(self) @cached_property def template_postgis(self): template_postgis = getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis') with self._nodb_connection.cursor() as cursor: cursor.execute('SELECT 1 FROM pg_database WHERE datname = %s LIMIT 1;', (template_postgis,)) if cursor.fetchone(): return template_postgis return None def prepare_database(self): super(DatabaseWrapper, self).prepare_database() if self.template_postgis is None: # Check that postgis extension is installed on PostGIS >= 2 # Check that postgis extension is installed. with self.cursor() as cursor: cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis") django/contrib/gis/db/backends/postgis/creation.pydeleted 100644 → 0 +0 −10 Original line number Diff line number Diff line from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation class PostGISCreation(DatabaseCreation): def sql_table_creation_suffix(self): if self.connection.template_postgis is not None: return ' TEMPLATE %s' % ( self.connection.ops.quote_name(self.connection.template_postgis),) return '' django/contrib/gis/db/backends/postgis/operations.py +7 −34 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.distance_spheroid = prefix + 'distance_spheroid' self.envelope = prefix + 'Envelope' self.extent = prefix + 'Extent' self.extent3d = prefix + '3DExtent' self.force_rhr = prefix + 'ForceRHR' self.geohash = prefix + 'GeoHash' self.geojson = prefix + 'AsGeoJson' Loading @@ -117,12 +118,14 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.intersection = prefix + 'Intersection' self.kml = prefix + 'AsKML' self.length = prefix + 'Length' self.length3d = prefix + '3DLength' self.length_spheroid = prefix + 'length_spheroid' self.makeline = prefix + 'MakeLine' self.mem_size = prefix + 'mem_size' self.num_geom = prefix + 'NumGeometries' self.num_points = prefix + 'npoints' self.perimeter = prefix + 'Perimeter' self.perimeter3d = prefix + '3DPerimeter' self.point_on_surface = prefix + 'PointOnSurface' self.polygonize = prefix + 'Polygonize' self.reverse = prefix + 'Reverse' Loading @@ -135,34 +138,6 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.union = prefix + 'Union' self.unionagg = prefix + 'Union' # Following "attributes" are properties due to the spatial_version check and # to delay database access @property def extent3d(self): if self.spatial_version >= (2, 0, 0): return self.geom_func_prefix + '3DExtent' else: return self.geom_func_prefix + 'Extent3D' @property def length3d(self): if self.spatial_version >= (2, 0, 0): return self.geom_func_prefix + '3DLength' else: return self.geom_func_prefix + 'Length3D' @property def perimeter3d(self): if self.spatial_version >= (2, 0, 0): return self.geom_func_prefix + '3DPerimeter' else: return self.geom_func_prefix + 'Perimeter3D' @property def geometry(self): # Native geometry type support added in PostGIS 2.0. return self.spatial_version >= (2, 0, 0) @cached_property def spatial_version(self): """Determine the version of the PostGIS library.""" Loading @@ -180,7 +155,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): except ProgrammingError: raise ImproperlyConfigured( 'Cannot determine PostGIS version for database "%s". ' 'GeoDjango requires at least PostGIS version 1.5. ' 'GeoDjango requires at least PostGIS version 2.0. ' 'Was the database created from a spatial database ' 'template?' % self.connection.settings_dict['NAME'] ) Loading Loading @@ -234,16 +209,14 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): raise NotImplementedError('PostGIS only supports geography columns with an SRID of 4326.') return 'geography(%s,%d)' % (f.geom_type, f.srid) elif self.geometry: # Postgis 2.0 supports type-based geometries. else: # 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: return None def get_distance(self, f, dist_val, lookup_type): """ Loading @@ -253,7 +226,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the geography column type newly introduced in PostGIS 1.5. the geography column type. """ # Getting the distance parameter and any options. if len(dist_val) == 1: Loading django/contrib/gis/db/backends/postgis/schema.py +5 −50 Original line number Diff line number Diff line Loading @@ -3,12 +3,8 @@ from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor class PostGISSchemaEditor(DatabaseSchemaEditor): geom_index_type = 'GIST' geom_index_ops = 'GIST_GEOMETRY_OPS' geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND' sql_add_geometry_column = "SELECT AddGeometryColumn(%(table)s, %(column)s, %(srid)s, %(geom_type)s, %(dim)s)" sql_drop_geometry_column = "SELECT DropGeometryColumn(%(table)s, %(column)s)" sql_alter_geometry_column_not_null = "ALTER TABLE %(table)s ALTER COLUMN %(column)s SET NOT NULL" sql_add_spatial_index = "CREATE INDEX %(index)s ON %(table)s USING %(index_type)s (%(column)s %(ops)s)" sql_clear_geometry_columns = "DELETE FROM geometry_columns WHERE f_table_name = %(table)s" Loading @@ -24,48 +20,21 @@ class PostGISSchemaEditor(DatabaseSchemaEditor): if not isinstance(field, GeometryField): return super(PostGISSchemaEditor, self).column_sql(model, field, include_default) if field.geography or self.connection.ops.geometry: # Geography and Geometry (PostGIS 2.0+) columns are # created normally. column_sql = super(PostGISSchemaEditor, self).column_sql(model, field, include_default) else: column_sql = None, None # Geometry columns are created by the `AddGeometryColumn` # stored procedure. self.geometry_sql.append( self.sql_add_geometry_column % { "table": self.geo_quote_name(model._meta.db_table), "column": self.geo_quote_name(field.column), "srid": field.srid, "geom_type": self.geo_quote_name(field.geom_type), "dim": field.dim, } ) if not field.null: self.geometry_sql.append( self.sql_alter_geometry_column_not_null % { "table": self.quote_name(model._meta.db_table), "column": self.quote_name(field.column), } ) if field.spatial_index: # Spatial indexes created the same way for both Geometry and # Geography columns. # PostGIS 2.0 does not support GIST_GEOMETRY_OPS. So, on 1.5 # we use GIST_GEOMETRY_OPS, on 2.0 we use either "nd" ops # which are fast on multidimensional cases, or just plain # gist index for the 2d case. if field.geography: index_ops = '' elif self.connection.ops.geometry: else: # Use either "nd" ops which are fast on multidimensional cases # or just plain gist index for the 2d case. if field.dim > 2: index_ops = self.geom_index_ops_nd else: index_ops = '' else: index_ops = self.geom_index_ops self.geometry_sql.append( self.sql_add_spatial_index % { "index": self.quote_name('%s_%s_id' % (model._meta.db_table, field.column)), Loading Loading @@ -96,17 +65,3 @@ class PostGISSchemaEditor(DatabaseSchemaEditor): for sql in self.geometry_sql: self.execute(sql) self.geometry_sql = [] def remove_field(self, model, field): from django.contrib.gis.db.models.fields import GeometryField if not isinstance(field, GeometryField) or \ self.connection.ops.spatial_version > (2, 0) or \ field.geography: super(PostGISSchemaEditor, self).remove_field(model, field) else: self.execute( self.sql_drop_geometry_column % { "table": self.geo_quote_name(model._meta.db_table), "column": self.geo_quote_name(field.column), } ) django/db/backends/base/schema.py +2 −2 Original line number Diff line number Diff line Loading @@ -462,8 +462,8 @@ class BaseDatabaseSchemaEditor(object): (new_type is None and new_field.remote_field is None)): raise ValueError( "Cannot alter field %s into %s - they do not properly define " "db_type (are you using PostGIS 1.5 or badly-written custom " "fields?)" % (old_field, new_field), "db_type (are you using a badly-written custom field?)" % (old_field, new_field), ) elif old_type is None and new_type is None and ( old_field.remote_field.through and new_field.remote_field.through and Loading Loading
django/contrib/gis/db/backends/postgis/base.py +3 −17 Original line number Diff line number Diff line from django.conf import settings from django.db.backends.base.base import NO_DB_ALIAS from django.db.backends.postgresql_psycopg2.base import \ DatabaseWrapper as Psycopg2DatabaseWrapper from django.utils.functional import cached_property from .creation import PostGISCreation from .features import DatabaseFeatures from .introspection import PostGISIntrospection from .operations import PostGISOperations Loading @@ -18,22 +15,11 @@ class DatabaseWrapper(Psycopg2DatabaseWrapper): super(DatabaseWrapper, self).__init__(*args, **kwargs) if kwargs.get('alias', '') != NO_DB_ALIAS: self.features = DatabaseFeatures(self) self.creation = PostGISCreation(self) self.ops = PostGISOperations(self) self.introspection = PostGISIntrospection(self) @cached_property def template_postgis(self): template_postgis = getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis') with self._nodb_connection.cursor() as cursor: cursor.execute('SELECT 1 FROM pg_database WHERE datname = %s LIMIT 1;', (template_postgis,)) if cursor.fetchone(): return template_postgis return None def prepare_database(self): super(DatabaseWrapper, self).prepare_database() if self.template_postgis is None: # Check that postgis extension is installed on PostGIS >= 2 # Check that postgis extension is installed. with self.cursor() as cursor: cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
django/contrib/gis/db/backends/postgis/creation.pydeleted 100644 → 0 +0 −10 Original line number Diff line number Diff line from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation class PostGISCreation(DatabaseCreation): def sql_table_creation_suffix(self): if self.connection.template_postgis is not None: return ' TEMPLATE %s' % ( self.connection.ops.quote_name(self.connection.template_postgis),) return ''
django/contrib/gis/db/backends/postgis/operations.py +7 −34 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.distance_spheroid = prefix + 'distance_spheroid' self.envelope = prefix + 'Envelope' self.extent = prefix + 'Extent' self.extent3d = prefix + '3DExtent' self.force_rhr = prefix + 'ForceRHR' self.geohash = prefix + 'GeoHash' self.geojson = prefix + 'AsGeoJson' Loading @@ -117,12 +118,14 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.intersection = prefix + 'Intersection' self.kml = prefix + 'AsKML' self.length = prefix + 'Length' self.length3d = prefix + '3DLength' self.length_spheroid = prefix + 'length_spheroid' self.makeline = prefix + 'MakeLine' self.mem_size = prefix + 'mem_size' self.num_geom = prefix + 'NumGeometries' self.num_points = prefix + 'npoints' self.perimeter = prefix + 'Perimeter' self.perimeter3d = prefix + '3DPerimeter' self.point_on_surface = prefix + 'PointOnSurface' self.polygonize = prefix + 'Polygonize' self.reverse = prefix + 'Reverse' Loading @@ -135,34 +138,6 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): self.union = prefix + 'Union' self.unionagg = prefix + 'Union' # Following "attributes" are properties due to the spatial_version check and # to delay database access @property def extent3d(self): if self.spatial_version >= (2, 0, 0): return self.geom_func_prefix + '3DExtent' else: return self.geom_func_prefix + 'Extent3D' @property def length3d(self): if self.spatial_version >= (2, 0, 0): return self.geom_func_prefix + '3DLength' else: return self.geom_func_prefix + 'Length3D' @property def perimeter3d(self): if self.spatial_version >= (2, 0, 0): return self.geom_func_prefix + '3DPerimeter' else: return self.geom_func_prefix + 'Perimeter3D' @property def geometry(self): # Native geometry type support added in PostGIS 2.0. return self.spatial_version >= (2, 0, 0) @cached_property def spatial_version(self): """Determine the version of the PostGIS library.""" Loading @@ -180,7 +155,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): except ProgrammingError: raise ImproperlyConfigured( 'Cannot determine PostGIS version for database "%s". ' 'GeoDjango requires at least PostGIS version 1.5. ' 'GeoDjango requires at least PostGIS version 2.0. ' 'Was the database created from a spatial database ' 'template?' % self.connection.settings_dict['NAME'] ) Loading Loading @@ -234,16 +209,14 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): raise NotImplementedError('PostGIS only supports geography columns with an SRID of 4326.') return 'geography(%s,%d)' % (f.geom_type, f.srid) elif self.geometry: # Postgis 2.0 supports type-based geometries. else: # 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: return None def get_distance(self, f, dist_val, lookup_type): """ Loading @@ -253,7 +226,7 @@ class PostGISOperations(BaseSpatialOperations, DatabaseOperations): This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the geography column type newly introduced in PostGIS 1.5. the geography column type. """ # Getting the distance parameter and any options. if len(dist_val) == 1: Loading
django/contrib/gis/db/backends/postgis/schema.py +5 −50 Original line number Diff line number Diff line Loading @@ -3,12 +3,8 @@ from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor class PostGISSchemaEditor(DatabaseSchemaEditor): geom_index_type = 'GIST' geom_index_ops = 'GIST_GEOMETRY_OPS' geom_index_ops_nd = 'GIST_GEOMETRY_OPS_ND' sql_add_geometry_column = "SELECT AddGeometryColumn(%(table)s, %(column)s, %(srid)s, %(geom_type)s, %(dim)s)" sql_drop_geometry_column = "SELECT DropGeometryColumn(%(table)s, %(column)s)" sql_alter_geometry_column_not_null = "ALTER TABLE %(table)s ALTER COLUMN %(column)s SET NOT NULL" sql_add_spatial_index = "CREATE INDEX %(index)s ON %(table)s USING %(index_type)s (%(column)s %(ops)s)" sql_clear_geometry_columns = "DELETE FROM geometry_columns WHERE f_table_name = %(table)s" Loading @@ -24,48 +20,21 @@ class PostGISSchemaEditor(DatabaseSchemaEditor): if not isinstance(field, GeometryField): return super(PostGISSchemaEditor, self).column_sql(model, field, include_default) if field.geography or self.connection.ops.geometry: # Geography and Geometry (PostGIS 2.0+) columns are # created normally. column_sql = super(PostGISSchemaEditor, self).column_sql(model, field, include_default) else: column_sql = None, None # Geometry columns are created by the `AddGeometryColumn` # stored procedure. self.geometry_sql.append( self.sql_add_geometry_column % { "table": self.geo_quote_name(model._meta.db_table), "column": self.geo_quote_name(field.column), "srid": field.srid, "geom_type": self.geo_quote_name(field.geom_type), "dim": field.dim, } ) if not field.null: self.geometry_sql.append( self.sql_alter_geometry_column_not_null % { "table": self.quote_name(model._meta.db_table), "column": self.quote_name(field.column), } ) if field.spatial_index: # Spatial indexes created the same way for both Geometry and # Geography columns. # PostGIS 2.0 does not support GIST_GEOMETRY_OPS. So, on 1.5 # we use GIST_GEOMETRY_OPS, on 2.0 we use either "nd" ops # which are fast on multidimensional cases, or just plain # gist index for the 2d case. if field.geography: index_ops = '' elif self.connection.ops.geometry: else: # Use either "nd" ops which are fast on multidimensional cases # or just plain gist index for the 2d case. if field.dim > 2: index_ops = self.geom_index_ops_nd else: index_ops = '' else: index_ops = self.geom_index_ops self.geometry_sql.append( self.sql_add_spatial_index % { "index": self.quote_name('%s_%s_id' % (model._meta.db_table, field.column)), Loading Loading @@ -96,17 +65,3 @@ class PostGISSchemaEditor(DatabaseSchemaEditor): for sql in self.geometry_sql: self.execute(sql) self.geometry_sql = [] def remove_field(self, model, field): from django.contrib.gis.db.models.fields import GeometryField if not isinstance(field, GeometryField) or \ self.connection.ops.spatial_version > (2, 0) or \ field.geography: super(PostGISSchemaEditor, self).remove_field(model, field) else: self.execute( self.sql_drop_geometry_column % { "table": self.geo_quote_name(model._meta.db_table), "column": self.geo_quote_name(field.column), } )
django/db/backends/base/schema.py +2 −2 Original line number Diff line number Diff line Loading @@ -462,8 +462,8 @@ class BaseDatabaseSchemaEditor(object): (new_type is None and new_field.remote_field is None)): raise ValueError( "Cannot alter field %s into %s - they do not properly define " "db_type (are you using PostGIS 1.5 or badly-written custom " "fields?)" % (old_field, new_field), "db_type (are you using a badly-written custom field?)" % (old_field, new_field), ) elif old_type is None and new_type is None and ( old_field.remote_field.through and new_field.remote_field.through and Loading