You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

schema.py 6.7KB

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