From 307de67073be44c70230ba6d7542560d24a2d13b Mon Sep 17 00:00:00 2001 From: Claude Paroz Date: Sat, 6 Dec 2014 11:05:54 +0100 Subject: [PATCH] Fixed #20968 -- Checked Spatialite metadata before migrations Thanks Kenial S. Lee for the initial patch. --- .../gis/db/backends/spatialite/base.py | 9 +++++ .../gis/db/backends/spatialite/creation.py | 8 ----- django/core/management/commands/migrate.py | 2 ++ django/db/backends/__init__.py | 7 ++++ django/db/backends/creation.py | 8 ----- docs/ref/contrib/gis/install/spatialite.txt | 35 ++++--------------- 6 files changed, 24 insertions(+), 45 deletions(-) diff --git a/django/contrib/gis/db/backends/spatialite/base.py b/django/contrib/gis/db/backends/spatialite/base.py index e51ffbef3a..63efb3ac61 100644 --- a/django/contrib/gis/db/backends/spatialite/base.py +++ b/django/contrib/gis/db/backends/spatialite/base.py @@ -79,3 +79,12 @@ class DatabaseWrapper(SQLiteDatabaseWrapper): six.reraise(ImproperlyConfigured, ImproperlyConfigured(new_msg), sys.exc_info()[2]) cur.close() return conn + + def prepare_database(self): + super(DatabaseWrapper, self).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() == []: + arg = "1" if self.features.supports_initspatialmetadata_in_one_transaction else "" + cursor.execute("SELECT InitSpatialMetaData(%s)" % arg) diff --git a/django/contrib/gis/db/backends/spatialite/creation.py b/django/contrib/gis/db/backends/spatialite/creation.py index 6c6ab98bfb..f1f40671d5 100644 --- a/django/contrib/gis/db/backends/spatialite/creation.py +++ b/django/contrib/gis/db/backends/spatialite/creation.py @@ -30,11 +30,3 @@ class SpatiaLiteCreation(DatabaseCreation): style.SQL_FIELD(gqn(f.column)) + ');') return output - - def _create_test_db_pre_migrate_sql(self): - """ - Creates the spatial metadata tables. - """ - cur = self.connection._cursor() - arg = "1" if self.connection.features.supports_initspatialmetadata_in_one_transaction else "" - cur.execute("SELECT InitSpatialMetaData(%s)" % arg) diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py index 536afb0bb4..00732390aa 100644 --- a/django/core/management/commands/migrate.py +++ b/django/core/management/commands/migrate.py @@ -64,6 +64,8 @@ class Command(BaseCommand): if options.get("list", False): return self.show_migration_list(connection, [options['app_label']] if options['app_label'] else None) + # Hook for backends needing any database preparation + connection.prepare_database() # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index dc4205e856..b9856031a8 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -432,6 +432,13 @@ class BaseDatabaseWrapper(object): ##### Miscellaneous ##### + def prepare_database(self): + """ + Hook to do any database check or preparation, generally called before + migrating a project or an app. + """ + pass + @cached_property def wrap_database_errors(self): """ diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index cf48b92df1..18168deaca 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -371,8 +371,6 @@ class BaseDatabaseCreation(object): settings.DATABASES[self.connection.alias]["NAME"] = test_database_name self.connection.settings_dict["NAME"] = test_database_name - self._create_test_db_pre_migrate_sql() - # We report migrate messages at one level lower than that requested. # This ensures we don't get flooded with messages during testing # (unless you really ask to be flooded). @@ -398,12 +396,6 @@ class BaseDatabaseCreation(object): return test_database_name - def _create_test_db_pre_migrate_sql(self): - """ - Hook for databases to load SQL before creating the test DB. - """ - pass - def serialize_db_to_string(self): """ Serializes all data in the database into a JSON string. diff --git a/docs/ref/contrib/gis/install/spatialite.txt b/docs/ref/contrib/gis/install/spatialite.txt index d7508a4fee..05833496aa 100644 --- a/docs/ref/contrib/gis/install/spatialite.txt +++ b/docs/ref/contrib/gis/install/spatialite.txt @@ -211,34 +211,11 @@ following to your ``settings.py``:: Creating a spatial database for SpatiaLite ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -After you've installed SpatiaLite, you'll need to create a number of spatial -metadata tables in your database in order to perform spatial queries. +When running ``manage.py migrate`` with a SQLite or SpatiaLite database, the +database file will be automatically created if it doesn't exist. Django will +also ensure that the spatial metadata are initialized in the database. -Use the ``spatialite`` utility to call the ``InitSpatialMetaData()`` function, -like this:: +.. versionchanged:: 1.8 - $ spatialite geodjango.db "SELECT InitSpatialMetaData();" - the SPATIAL_REF_SYS table already contains some row(s) - InitSpatiaMetaData ()error:"table spatial_ref_sys already exists" - 0 - -You can safely ignore the error messages shown. - -.. note:: - - The parameter ``geodjango.db`` is the *filename* of the SQLite database - you want to use. Use the same in the :setting:`DATABASES` ``"name"`` key - inside your ``settings.py``. - -.. note:: - - When running ``manage.py migrate`` with a SQLite (or SpatiaLite) database, - the database file will be automatically created if it doesn't exist. In - this case, if your models contain any geometry columns, you'll see this - error:: - - CreateSpatialIndex() error: "no such table: geometry_columns" - - It's because the table creation queries are executed without spatial - metadata tables. To avoid this, make the database file before executing - ``manage.py migrate`` as described above. + Prior to Django 1.8, you had to intialize spatial metadata tables yourself + by manually running the "SELECT InitSpatialMetaData();" query.