schema.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. from django.db.utils import DatabaseError
  2. from django.db.backends.sqlite3.schema import DatabaseSchemaEditor
  3. class SpatialiteSchemaEditor(DatabaseSchemaEditor):
  4. sql_add_geometry_column = "SELECT AddGeometryColumn(%(table)s, %(column)s, %(srid)s, %(geom_type)s, %(dim)s, %(null)s)"
  5. sql_add_spatial_index = "SELECT CreateSpatialIndex(%(table)s, %(column)s)"
  6. sql_drop_spatial_index = "DROP TABLE idx_%(table)s_%(column)s"
  7. sql_remove_geometry_metadata = "SELECT DiscardGeometryColumn(%(table)s, %(column)s)"
  8. sql_discard_geometry_columns = "DELETE FROM %(geom_table)s WHERE f_table_name = %(table)s"
  9. sql_update_geometry_columns = "UPDATE %(geom_table)s SET f_table_name = %(new_table)s WHERE f_table_name = %(old_table)s"
  10. geometry_tables = [
  11. "geometry_columns",
  12. "geometry_columns_auth",
  13. "geometry_columns_time",
  14. "geometry_columns_statistics",
  15. ]
  16. def __init__(self, *args, **kwargs):
  17. super(SpatialiteSchemaEditor, self).__init__(*args, **kwargs)
  18. self.geometry_sql = []
  19. def geo_quote_name(self, name):
  20. return self.connection.ops.geo_quote_name(name)
  21. def column_sql(self, model, field, include_default=False):
  22. from django.contrib.gis.db.models.fields import GeometryField
  23. if not isinstance(field, GeometryField):
  24. return super(SpatialiteSchemaEditor, self).column_sql(model, field, include_default)
  25. # Geometry columns are created by the `AddGeometryColumn` function
  26. self.geometry_sql.append(
  27. self.sql_add_geometry_column % {
  28. "table": self.geo_quote_name(model._meta.db_table),
  29. "column": self.geo_quote_name(field.column),
  30. "srid": field.srid,
  31. "geom_type": self.geo_quote_name(field.geom_type),
  32. "dim": field.dim,
  33. "null": int(not field.null),
  34. }
  35. )
  36. if field.spatial_index:
  37. self.geometry_sql.append(
  38. self.sql_add_spatial_index % {
  39. "table": self.quote_name(model._meta.db_table),
  40. "column": self.quote_name(field.column),
  41. }
  42. )
  43. return None, None
  44. def remove_geometry_metadata(self, model, field):
  45. self.execute(
  46. self.sql_remove_geometry_metadata % {
  47. "table": self.quote_name(model._meta.db_table),
  48. "column": self.quote_name(field.column),
  49. }
  50. )
  51. self.execute(
  52. self.sql_drop_spatial_index % {
  53. "table": model._meta.db_table,
  54. "column": field.column,
  55. }
  56. )
  57. def create_model(self, model):
  58. super(SpatialiteSchemaEditor, self).create_model(model)
  59. # Create geometry columns
  60. for sql in self.geometry_sql:
  61. self.execute(sql)
  62. self.geometry_sql = []
  63. def delete_model(self, model, **kwargs):
  64. from django.contrib.gis.db.models.fields import GeometryField
  65. # Drop spatial metadata (dropping the table does not automatically remove them)
  66. for field in model._meta.local_fields:
  67. if isinstance(field, GeometryField):
  68. self.remove_geometry_metadata(model, field)
  69. # Make sure all geom stuff is gone
  70. for geom_table in self.geometry_tables:
  71. try:
  72. self.execute(
  73. self.sql_discard_geometry_columns % {
  74. "geom_table": geom_table,
  75. "table": self.quote_name(model._meta.db_table),
  76. }
  77. )
  78. except DatabaseError:
  79. pass
  80. super(SpatialiteSchemaEditor, self).delete_model(model, **kwargs)
  81. def add_field(self, model, field):
  82. from django.contrib.gis.db.models.fields import GeometryField
  83. if isinstance(field, GeometryField):
  84. # Populate self.geometry_sql
  85. self.column_sql(model, field)
  86. for sql in self.geometry_sql:
  87. self.execute(sql)
  88. self.geometry_sql = []
  89. else:
  90. super(SpatialiteSchemaEditor, self).add_field(model, field)
  91. def remove_field(self, model, field):
  92. from django.contrib.gis.db.models.fields import GeometryField
  93. # NOTE: If the field is a geometry field, the table is just recreated,
  94. # the parent's remove_field can't be used cause it will skip the
  95. # recreation if the field does not have a database type. Geometry fields
  96. # do not have a db type cause they are added and removed via stored
  97. # procedures.
  98. if isinstance(field, GeometryField):
  99. self._remake_table(model, delete_fields=[field])
  100. else:
  101. super(SpatialiteSchemaEditor, self).remove_field(model, field)
  102. def alter_db_table(self, model, old_db_table, new_db_table):
  103. from django.contrib.gis.db.models.fields import GeometryField
  104. # Remove geometry-ness from temp table
  105. for field in model._meta.local_fields:
  106. if isinstance(field, GeometryField):
  107. self.execute(
  108. self.sql_remove_geometry_metadata % {
  109. "table": self.quote_name(old_db_table),
  110. "column": self.quote_name(field.column),
  111. }
  112. )
  113. # Alter table
  114. super(SpatialiteSchemaEditor, self).alter_db_table(model, old_db_table, new_db_table)
  115. # Repoint any straggler names
  116. for geom_table in self.geometry_tables:
  117. try:
  118. self.execute(
  119. self.sql_update_geometry_columns % {
  120. "geom_table": geom_table,
  121. "old_table": self.quote_name(old_db_table),
  122. "new_table": self.quote_name(new_db_table),
  123. }
  124. )
  125. except DatabaseError:
  126. pass
  127. # Re-add geometry-ness and rename spatial index tables
  128. for field in model._meta.local_fields:
  129. if isinstance(field, GeometryField):
  130. self.execute(self.sql_add_geometry_column % {
  131. "table": self.geo_quote_name(new_db_table),
  132. "column": self.geo_quote_name(field.column),
  133. "srid": field.srid,
  134. "geom_type": self.geo_quote_name(field.geom_type),
  135. "dim": field.dim,
  136. "null": int(not field.null),
  137. })
  138. if getattr(field, 'spatial_index', False):
  139. self.execute(self.sql_rename_table % {
  140. "old_table": self.quote_name("idx_%s_%s" % (old_db_table, field.column)),
  141. "new_table": self.quote_name("idx_%s_%s" % (new_db_table, field.column)),
  142. })