Ajoutez des fichiers projet.
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
from django.contrib.gis.db.backends.base.adapter import WKTAdapter
|
||||
from django.db.backends.sqlite3.base import Database
|
||||
|
||||
|
||||
class SpatiaLiteAdapter(WKTAdapter):
|
||||
"SQLite adapter for geometry objects."
|
||||
def __conform__(self, protocol):
|
||||
if protocol is Database.PrepareProtocol:
|
||||
return str(self)
|
@@ -0,0 +1,77 @@
|
||||
from ctypes.util import find_library
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db.backends.sqlite3.base import (
|
||||
DatabaseWrapper as SQLiteDatabaseWrapper,
|
||||
)
|
||||
|
||||
from .client import SpatiaLiteClient
|
||||
from .features import DatabaseFeatures
|
||||
from .introspection import SpatiaLiteIntrospection
|
||||
from .operations import SpatiaLiteOperations
|
||||
from .schema import SpatialiteSchemaEditor
|
||||
|
||||
|
||||
class DatabaseWrapper(SQLiteDatabaseWrapper):
|
||||
SchemaEditorClass = SpatialiteSchemaEditor
|
||||
# Classes instantiated in __init__().
|
||||
client_class = SpatiaLiteClient
|
||||
features_class = DatabaseFeatures
|
||||
introspection_class = SpatiaLiteIntrospection
|
||||
ops_class = SpatiaLiteOperations
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Trying to find the location of the SpatiaLite library.
|
||||
# Here we are figuring out the path to the SpatiaLite library
|
||||
# (`libspatialite`). If it's not in the system library path (e.g., it
|
||||
# cannot be found by `ctypes.util.find_library`), then it may be set
|
||||
# manually in the settings via the `SPATIALITE_LIBRARY_PATH` setting.
|
||||
self.lib_spatialite_paths = [name for name in [
|
||||
getattr(settings, 'SPATIALITE_LIBRARY_PATH', None),
|
||||
'mod_spatialite.so',
|
||||
'mod_spatialite',
|
||||
find_library('spatialite'),
|
||||
] if name is not None]
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_new_connection(self, conn_params):
|
||||
conn = super().get_new_connection(conn_params)
|
||||
# Enabling extension loading on the SQLite connection.
|
||||
try:
|
||||
conn.enable_load_extension(True)
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
'SpatiaLite requires SQLite to be configured to allow '
|
||||
'extension loading.'
|
||||
)
|
||||
# Load the SpatiaLite library extension on the connection.
|
||||
for path in self.lib_spatialite_paths:
|
||||
try:
|
||||
conn.load_extension(path)
|
||||
except Exception:
|
||||
if getattr(settings, 'SPATIALITE_LIBRARY_PATH', None):
|
||||
raise ImproperlyConfigured(
|
||||
'Unable to load the SpatiaLite library extension '
|
||||
'as specified in your SPATIALITE_LIBRARY_PATH setting.'
|
||||
)
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
'Unable to load the SpatiaLite library extension. '
|
||||
'Library names tried: %s' % ', '.join(self.lib_spatialite_paths)
|
||||
)
|
||||
return conn
|
||||
|
||||
def prepare_database(self):
|
||||
super().prepare_database()
|
||||
# Check if spatial metadata have been initialized in the database
|
||||
with self.cursor() as cursor:
|
||||
cursor.execute("PRAGMA table_info(geometry_columns);")
|
||||
if cursor.fetchall() == []:
|
||||
if self.ops.spatial_version < (5,):
|
||||
cursor.execute('SELECT InitSpatialMetaData(1)')
|
||||
else:
|
||||
cursor.execute('SELECT InitSpatialMetaDataFull(1)')
|
@@ -0,0 +1,5 @@
|
||||
from django.db.backends.sqlite3.client import DatabaseClient
|
||||
|
||||
|
||||
class SpatiaLiteClient(DatabaseClient):
|
||||
executable_name = 'spatialite'
|
@@ -0,0 +1,24 @@
|
||||
from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures
|
||||
from django.db.backends.sqlite3.features import (
|
||||
DatabaseFeatures as SQLiteDatabaseFeatures,
|
||||
)
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
class DatabaseFeatures(BaseSpatialFeatures, SQLiteDatabaseFeatures):
|
||||
can_alter_geometry_field = False # Not implemented
|
||||
supports_3d_storage = True
|
||||
|
||||
@cached_property
|
||||
def supports_area_geodetic(self):
|
||||
return bool(self.connection.ops.geom_lib_version())
|
||||
|
||||
@cached_property
|
||||
def django_test_skips(self):
|
||||
skips = super().django_test_skips
|
||||
skips.update({
|
||||
"SpatiaLite doesn't support distance lookups with Distance objects.": {
|
||||
'gis_tests.geogapp.tests.GeographyTest.test02_distance_lookup',
|
||||
},
|
||||
})
|
||||
return skips
|
@@ -0,0 +1,73 @@
|
||||
from django.contrib.gis.gdal import OGRGeomType
|
||||
from django.db.backends.sqlite3.introspection import (
|
||||
DatabaseIntrospection, FlexibleFieldLookupDict,
|
||||
)
|
||||
|
||||
|
||||
class GeoFlexibleFieldLookupDict(FlexibleFieldLookupDict):
|
||||
"""
|
||||
Subclass that includes updates the `base_data_types_reverse` dict
|
||||
for geometry field types.
|
||||
"""
|
||||
base_data_types_reverse = {
|
||||
**FlexibleFieldLookupDict.base_data_types_reverse,
|
||||
'point': 'GeometryField',
|
||||
'linestring': 'GeometryField',
|
||||
'polygon': 'GeometryField',
|
||||
'multipoint': 'GeometryField',
|
||||
'multilinestring': 'GeometryField',
|
||||
'multipolygon': 'GeometryField',
|
||||
'geometrycollection': 'GeometryField',
|
||||
}
|
||||
|
||||
|
||||
class SpatiaLiteIntrospection(DatabaseIntrospection):
|
||||
data_types_reverse = GeoFlexibleFieldLookupDict()
|
||||
|
||||
def get_geometry_type(self, table_name, description):
|
||||
with self.connection.cursor() as cursor:
|
||||
# Querying the `geometry_columns` table to get additional metadata.
|
||||
cursor.execute('SELECT coord_dimension, srid, geometry_type '
|
||||
'FROM geometry_columns '
|
||||
'WHERE f_table_name=%s AND f_geometry_column=%s',
|
||||
(table_name, description.name))
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
raise Exception('Could not find a geometry column for "%s"."%s"' %
|
||||
(table_name, description.name))
|
||||
|
||||
# OGRGeomType does not require GDAL and makes it easy to convert
|
||||
# from OGC geom type name to Django field.
|
||||
ogr_type = row[2]
|
||||
if isinstance(ogr_type, int) and ogr_type > 1000:
|
||||
# SpatiaLite uses SFSQL 1.2 offsets 1000 (Z), 2000 (M), and
|
||||
# 3000 (ZM) to indicate the presence of higher dimensional
|
||||
# coordinates (M not yet supported by Django).
|
||||
ogr_type = ogr_type % 1000 + OGRGeomType.wkb25bit
|
||||
field_type = OGRGeomType(ogr_type).django
|
||||
|
||||
# Getting any GeometryField keyword arguments that are not the default.
|
||||
dim = row[0]
|
||||
srid = row[1]
|
||||
field_params = {}
|
||||
if srid != 4326:
|
||||
field_params['srid'] = srid
|
||||
if (isinstance(dim, str) and 'Z' in dim) or dim == 3:
|
||||
field_params['dim'] = 3
|
||||
return field_type, field_params
|
||||
|
||||
def get_constraints(self, cursor, table_name):
|
||||
constraints = super().get_constraints(cursor, table_name)
|
||||
cursor.execute('SELECT f_geometry_column '
|
||||
'FROM geometry_columns '
|
||||
'WHERE f_table_name=%s AND spatial_index_enabled=1', (table_name,))
|
||||
for row in cursor.fetchall():
|
||||
constraints['%s__spatial__index' % row[0]] = {
|
||||
"columns": [row[0]],
|
||||
"primary_key": False,
|
||||
"unique": False,
|
||||
"foreign_key": None,
|
||||
"check": False,
|
||||
"index": True,
|
||||
}
|
||||
return constraints
|
@@ -0,0 +1,68 @@
|
||||
"""
|
||||
The GeometryColumns and SpatialRefSys models for the SpatiaLite backend.
|
||||
"""
|
||||
from django.contrib.gis.db.backends.base.models import SpatialRefSysMixin
|
||||
from django.db import models
|
||||
|
||||
|
||||
class SpatialiteGeometryColumns(models.Model):
|
||||
"""
|
||||
The 'geometry_columns' table from SpatiaLite.
|
||||
"""
|
||||
f_table_name = models.CharField(max_length=256)
|
||||
f_geometry_column = models.CharField(max_length=256)
|
||||
coord_dimension = models.IntegerField()
|
||||
srid = models.IntegerField(primary_key=True)
|
||||
spatial_index_enabled = models.IntegerField()
|
||||
type = models.IntegerField(db_column='geometry_type')
|
||||
|
||||
class Meta:
|
||||
app_label = 'gis'
|
||||
db_table = 'geometry_columns'
|
||||
managed = False
|
||||
|
||||
def __str__(self):
|
||||
return '%s.%s - %dD %s field (SRID: %d)' % (
|
||||
self.f_table_name,
|
||||
self.f_geometry_column,
|
||||
self.coord_dimension,
|
||||
self.type,
|
||||
self.srid,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def table_name_col(cls):
|
||||
"""
|
||||
Return the name of the metadata column used to store the feature table
|
||||
name.
|
||||
"""
|
||||
return 'f_table_name'
|
||||
|
||||
@classmethod
|
||||
def geom_col_name(cls):
|
||||
"""
|
||||
Return the name of the metadata column used to store the feature
|
||||
geometry column.
|
||||
"""
|
||||
return 'f_geometry_column'
|
||||
|
||||
|
||||
class SpatialiteSpatialRefSys(models.Model, SpatialRefSysMixin):
|
||||
"""
|
||||
The 'spatial_ref_sys' table from SpatiaLite.
|
||||
"""
|
||||
srid = models.IntegerField(primary_key=True)
|
||||
auth_name = models.CharField(max_length=256)
|
||||
auth_srid = models.IntegerField()
|
||||
ref_sys_name = models.CharField(max_length=256)
|
||||
proj4text = models.CharField(max_length=2048)
|
||||
srtext = models.CharField(max_length=2048)
|
||||
|
||||
class Meta:
|
||||
app_label = 'gis'
|
||||
db_table = 'spatial_ref_sys'
|
||||
managed = False
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
return self.srtext
|
@@ -0,0 +1,223 @@
|
||||
"""
|
||||
SQL functions reference lists:
|
||||
https://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html
|
||||
"""
|
||||
from django.contrib.gis.db import models
|
||||
from django.contrib.gis.db.backends.base.operations import (
|
||||
BaseSpatialOperations,
|
||||
)
|
||||
from django.contrib.gis.db.backends.spatialite.adapter import SpatiaLiteAdapter
|
||||
from django.contrib.gis.db.backends.utils import SpatialOperator
|
||||
from django.contrib.gis.geos.geometry import GEOSGeometry, GEOSGeometryBase
|
||||
from django.contrib.gis.geos.prototypes.io import wkb_r
|
||||
from django.contrib.gis.measure import Distance
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db.backends.sqlite3.operations import DatabaseOperations
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.version import get_version_tuple
|
||||
|
||||
|
||||
class SpatialiteNullCheckOperator(SpatialOperator):
|
||||
def as_sql(self, connection, lookup, template_params, sql_params):
|
||||
sql, params = super().as_sql(connection, lookup, template_params, sql_params)
|
||||
return '%s > 0' % sql, params
|
||||
|
||||
|
||||
class SpatiaLiteOperations(BaseSpatialOperations, DatabaseOperations):
|
||||
name = 'spatialite'
|
||||
spatialite = True
|
||||
|
||||
Adapter = SpatiaLiteAdapter
|
||||
|
||||
collect = 'Collect'
|
||||
extent = 'Extent'
|
||||
makeline = 'MakeLine'
|
||||
unionagg = 'GUnion'
|
||||
|
||||
from_text = 'GeomFromText'
|
||||
|
||||
gis_operators = {
|
||||
# Binary predicates
|
||||
'equals': SpatialiteNullCheckOperator(func='Equals'),
|
||||
'disjoint': SpatialiteNullCheckOperator(func='Disjoint'),
|
||||
'touches': SpatialiteNullCheckOperator(func='Touches'),
|
||||
'crosses': SpatialiteNullCheckOperator(func='Crosses'),
|
||||
'within': SpatialiteNullCheckOperator(func='Within'),
|
||||
'overlaps': SpatialiteNullCheckOperator(func='Overlaps'),
|
||||
'contains': SpatialiteNullCheckOperator(func='Contains'),
|
||||
'intersects': SpatialiteNullCheckOperator(func='Intersects'),
|
||||
'relate': SpatialiteNullCheckOperator(func='Relate'),
|
||||
'coveredby': SpatialiteNullCheckOperator(func='CoveredBy'),
|
||||
'covers': SpatialiteNullCheckOperator(func='Covers'),
|
||||
# Returns true if B's bounding box completely contains A's bounding box.
|
||||
'contained': SpatialOperator(func='MbrWithin'),
|
||||
# Returns true if A's bounding box completely contains B's bounding box.
|
||||
'bbcontains': SpatialOperator(func='MbrContains'),
|
||||
# Returns true if A's bounding box overlaps B's bounding box.
|
||||
'bboverlaps': SpatialOperator(func='MbrOverlaps'),
|
||||
# These are implemented here as synonyms for Equals
|
||||
'same_as': SpatialiteNullCheckOperator(func='Equals'),
|
||||
'exact': SpatialiteNullCheckOperator(func='Equals'),
|
||||
# Distance predicates
|
||||
'dwithin': SpatialOperator(func='PtDistWithin'),
|
||||
}
|
||||
|
||||
disallowed_aggregates = (models.Extent3D,)
|
||||
|
||||
select = 'CAST (AsEWKB(%s) AS BLOB)'
|
||||
|
||||
function_names = {
|
||||
'AsWKB': 'St_AsBinary',
|
||||
'ForcePolygonCW': 'ST_ForceLHR',
|
||||
'Length': 'ST_Length',
|
||||
'LineLocatePoint': 'ST_Line_Locate_Point',
|
||||
'NumPoints': 'ST_NPoints',
|
||||
'Reverse': 'ST_Reverse',
|
||||
'Scale': 'ScaleCoords',
|
||||
'Translate': 'ST_Translate',
|
||||
'Union': 'ST_Union',
|
||||
}
|
||||
|
||||
@cached_property
|
||||
def unsupported_functions(self):
|
||||
unsupported = {'BoundingCircle', 'GeometryDistance', 'MemSize'}
|
||||
if not self.geom_lib_version():
|
||||
unsupported |= {'Azimuth', 'GeoHash', 'MakeValid'}
|
||||
return unsupported
|
||||
|
||||
@cached_property
|
||||
def spatial_version(self):
|
||||
"""Determine the version of the SpatiaLite library."""
|
||||
try:
|
||||
version = self.spatialite_version_tuple()[1:]
|
||||
except Exception as exc:
|
||||
raise ImproperlyConfigured(
|
||||
'Cannot determine the SpatiaLite version for the "%s" database. '
|
||||
'Was the SpatiaLite initialization SQL loaded on this database?' % (
|
||||
self.connection.settings_dict['NAME'],
|
||||
)
|
||||
) from exc
|
||||
if version < (4, 3, 0):
|
||||
raise ImproperlyConfigured('GeoDjango supports SpatiaLite 4.3.0 and above.')
|
||||
return version
|
||||
|
||||
def convert_extent(self, box):
|
||||
"""
|
||||
Convert the polygon data received from SpatiaLite to min/max values.
|
||||
"""
|
||||
if box is None:
|
||||
return None
|
||||
shell = GEOSGeometry(box).shell
|
||||
xmin, ymin = shell[0][:2]
|
||||
xmax, ymax = shell[2][:2]
|
||||
return (xmin, ymin, xmax, ymax)
|
||||
|
||||
def geo_db_type(self, f):
|
||||
"""
|
||||
Return None because geometry columns are added via the
|
||||
`AddGeometryColumn` stored procedure on SpatiaLite.
|
||||
"""
|
||||
return None
|
||||
|
||||
def get_distance(self, f, value, lookup_type):
|
||||
"""
|
||||
Return the distance parameters for the given geometry field,
|
||||
lookup value, and lookup type.
|
||||
"""
|
||||
if not value:
|
||||
return []
|
||||
value = value[0]
|
||||
if isinstance(value, Distance):
|
||||
if f.geodetic(self.connection):
|
||||
if lookup_type == 'dwithin':
|
||||
raise ValueError(
|
||||
'Only numeric values of degree units are allowed on '
|
||||
'geographic DWithin queries.'
|
||||
)
|
||||
dist_param = value.m
|
||||
else:
|
||||
dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection)))
|
||||
else:
|
||||
dist_param = value
|
||||
return [dist_param]
|
||||
|
||||
def _get_spatialite_func(self, func):
|
||||
"""
|
||||
Helper routine for calling SpatiaLite functions and returning
|
||||
their result.
|
||||
Any error occurring in this method should be handled by the caller.
|
||||
"""
|
||||
cursor = self.connection._cursor()
|
||||
try:
|
||||
cursor.execute('SELECT %s' % func)
|
||||
row = cursor.fetchone()
|
||||
finally:
|
||||
cursor.close()
|
||||
return row[0]
|
||||
|
||||
def geos_version(self):
|
||||
"Return the version of GEOS used by SpatiaLite as a string."
|
||||
return self._get_spatialite_func('geos_version()')
|
||||
|
||||
def proj_version(self):
|
||||
"""Return the version of the PROJ library used by SpatiaLite."""
|
||||
return self._get_spatialite_func('proj4_version()')
|
||||
|
||||
def lwgeom_version(self):
|
||||
"""Return the version of LWGEOM library used by SpatiaLite."""
|
||||
return self._get_spatialite_func('lwgeom_version()')
|
||||
|
||||
def rttopo_version(self):
|
||||
"""Return the version of RTTOPO library used by SpatiaLite."""
|
||||
return self._get_spatialite_func('rttopo_version()')
|
||||
|
||||
def geom_lib_version(self):
|
||||
"""
|
||||
Return the version of the version-dependant geom library used by
|
||||
SpatiaLite.
|
||||
"""
|
||||
if self.spatial_version >= (5,):
|
||||
return self.rttopo_version()
|
||||
else:
|
||||
return self.lwgeom_version()
|
||||
|
||||
def spatialite_version(self):
|
||||
"Return the SpatiaLite library version as a string."
|
||||
return self._get_spatialite_func('spatialite_version()')
|
||||
|
||||
def spatialite_version_tuple(self):
|
||||
"""
|
||||
Return the SpatiaLite version as a tuple (version string, major,
|
||||
minor, subminor).
|
||||
"""
|
||||
version = self.spatialite_version()
|
||||
return (version,) + get_version_tuple(version)
|
||||
|
||||
def spatial_aggregate_name(self, agg_name):
|
||||
"""
|
||||
Return the spatial aggregate SQL template and function for the
|
||||
given Aggregate instance.
|
||||
"""
|
||||
agg_name = 'unionagg' if agg_name.lower() == 'union' else agg_name.lower()
|
||||
return getattr(self, agg_name)
|
||||
|
||||
# Routines for getting the OGC-compliant models.
|
||||
def geometry_columns(self):
|
||||
from django.contrib.gis.db.backends.spatialite.models import (
|
||||
SpatialiteGeometryColumns,
|
||||
)
|
||||
return SpatialiteGeometryColumns
|
||||
|
||||
def spatial_ref_sys(self):
|
||||
from django.contrib.gis.db.backends.spatialite.models import (
|
||||
SpatialiteSpatialRefSys,
|
||||
)
|
||||
return SpatialiteSpatialRefSys
|
||||
|
||||
def get_geometry_converter(self, expression):
|
||||
geom_class = expression.output_field.geom_class
|
||||
read = wkb_r().read
|
||||
|
||||
def converter(value, expression, connection):
|
||||
return None if value is None else GEOSGeometryBase(read(value), geom_class)
|
||||
return converter
|
@@ -0,0 +1,168 @@
|
||||
from django.db import DatabaseError
|
||||
from django.db.backends.sqlite3.schema import DatabaseSchemaEditor
|
||||
|
||||
|
||||
class SpatialiteSchemaEditor(DatabaseSchemaEditor):
|
||||
sql_add_geometry_column = (
|
||||
"SELECT AddGeometryColumn(%(table)s, %(column)s, %(srid)s, "
|
||||
"%(geom_type)s, %(dim)s, %(null)s)"
|
||||
)
|
||||
sql_add_spatial_index = "SELECT CreateSpatialIndex(%(table)s, %(column)s)"
|
||||
sql_drop_spatial_index = "DROP TABLE idx_%(table)s_%(column)s"
|
||||
sql_recover_geometry_metadata = (
|
||||
"SELECT RecoverGeometryColumn(%(table)s, %(column)s, %(srid)s, "
|
||||
"%(geom_type)s, %(dim)s)"
|
||||
)
|
||||
sql_remove_geometry_metadata = "SELECT DiscardGeometryColumn(%(table)s, %(column)s)"
|
||||
sql_discard_geometry_columns = "DELETE FROM %(geom_table)s WHERE f_table_name = %(table)s"
|
||||
sql_update_geometry_columns = (
|
||||
"UPDATE %(geom_table)s SET f_table_name = %(new_table)s "
|
||||
"WHERE f_table_name = %(old_table)s"
|
||||
)
|
||||
|
||||
geometry_tables = [
|
||||
"geometry_columns",
|
||||
"geometry_columns_auth",
|
||||
"geometry_columns_time",
|
||||
"geometry_columns_statistics",
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__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):
|
||||
from django.contrib.gis.db.models import GeometryField
|
||||
if not isinstance(field, GeometryField):
|
||||
return super().column_sql(model, field, include_default)
|
||||
|
||||
# Geometry columns are created by the `AddGeometryColumn` function
|
||||
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,
|
||||
"null": int(not field.null),
|
||||
}
|
||||
)
|
||||
|
||||
if field.spatial_index:
|
||||
self.geometry_sql.append(
|
||||
self.sql_add_spatial_index % {
|
||||
"table": self.quote_name(model._meta.db_table),
|
||||
"column": self.quote_name(field.column),
|
||||
}
|
||||
)
|
||||
return None, None
|
||||
|
||||
def remove_geometry_metadata(self, model, field):
|
||||
self.execute(
|
||||
self.sql_remove_geometry_metadata % {
|
||||
"table": self.quote_name(model._meta.db_table),
|
||||
"column": self.quote_name(field.column),
|
||||
}
|
||||
)
|
||||
self.execute(
|
||||
self.sql_drop_spatial_index % {
|
||||
"table": model._meta.db_table,
|
||||
"column": field.column,
|
||||
}
|
||||
)
|
||||
|
||||
def create_model(self, model):
|
||||
super().create_model(model)
|
||||
# Create geometry columns
|
||||
for sql in self.geometry_sql:
|
||||
self.execute(sql)
|
||||
self.geometry_sql = []
|
||||
|
||||
def delete_model(self, model, **kwargs):
|
||||
from django.contrib.gis.db.models import GeometryField
|
||||
|
||||
# Drop spatial metadata (dropping the table does not automatically remove them)
|
||||
for field in model._meta.local_fields:
|
||||
if isinstance(field, GeometryField):
|
||||
self.remove_geometry_metadata(model, field)
|
||||
# Make sure all geom stuff is gone
|
||||
for geom_table in self.geometry_tables:
|
||||
try:
|
||||
self.execute(
|
||||
self.sql_discard_geometry_columns % {
|
||||
"geom_table": geom_table,
|
||||
"table": self.quote_name(model._meta.db_table),
|
||||
}
|
||||
)
|
||||
except DatabaseError:
|
||||
pass
|
||||
super().delete_model(model, **kwargs)
|
||||
|
||||
def add_field(self, model, field):
|
||||
from django.contrib.gis.db.models import GeometryField
|
||||
if isinstance(field, GeometryField):
|
||||
# Populate self.geometry_sql
|
||||
self.column_sql(model, field)
|
||||
for sql in self.geometry_sql:
|
||||
self.execute(sql)
|
||||
self.geometry_sql = []
|
||||
else:
|
||||
super().add_field(model, field)
|
||||
|
||||
def remove_field(self, model, field):
|
||||
from django.contrib.gis.db.models import GeometryField
|
||||
|
||||
# NOTE: If the field is a geometry field, the table is just recreated,
|
||||
# the parent's remove_field can't be used cause it will skip the
|
||||
# recreation if the field does not have a database type. Geometry fields
|
||||
# do not have a db type cause they are added and removed via stored
|
||||
# procedures.
|
||||
if isinstance(field, GeometryField):
|
||||
self._remake_table(model, delete_field=field)
|
||||
else:
|
||||
super().remove_field(model, field)
|
||||
|
||||
def alter_db_table(self, model, old_db_table, new_db_table, disable_constraints=True):
|
||||
from django.contrib.gis.db.models import GeometryField
|
||||
|
||||
# Remove geometry-ness from temp table
|
||||
for field in model._meta.local_fields:
|
||||
if isinstance(field, GeometryField):
|
||||
self.execute(
|
||||
self.sql_remove_geometry_metadata % {
|
||||
"table": self.quote_name(old_db_table),
|
||||
"column": self.quote_name(field.column),
|
||||
}
|
||||
)
|
||||
# Alter table
|
||||
super().alter_db_table(model, old_db_table, new_db_table, disable_constraints)
|
||||
# Repoint any straggler names
|
||||
for geom_table in self.geometry_tables:
|
||||
try:
|
||||
self.execute(
|
||||
self.sql_update_geometry_columns % {
|
||||
"geom_table": geom_table,
|
||||
"old_table": self.quote_name(old_db_table),
|
||||
"new_table": self.quote_name(new_db_table),
|
||||
}
|
||||
)
|
||||
except DatabaseError:
|
||||
pass
|
||||
# Re-add geometry-ness and rename spatial index tables
|
||||
for field in model._meta.local_fields:
|
||||
if isinstance(field, GeometryField):
|
||||
self.execute(self.sql_recover_geometry_metadata % {
|
||||
"table": self.geo_quote_name(new_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 getattr(field, 'spatial_index', False):
|
||||
self.execute(self.sql_rename_table % {
|
||||
"old_table": self.quote_name("idx_%s_%s" % (old_db_table, field.column)),
|
||||
"new_table": self.quote_name("idx_%s_%s" % (new_db_table, field.column)),
|
||||
})
|
Reference in New Issue
Block a user