added support for postgis #5

Open
wants to merge 2 commits into
from
Jump to file or symbol
Failed to load files and symbols.
+214 −0
Split
No changes.
@@ -0,0 +1,147 @@
+import logging
+import re
+import sys
+import traceback
+from django import VERSION
+from django.conf import settings
+from django.contrib.gis.db.backends.postgis.base import *
+from django_hstore.backends.postgis.creation import PostGISCreation
+from django.db.backends.util import truncate_name
+from psycopg2.extras import register_hstore
+
+
+log = logging.getLogger(__name__)
+# Regexp for SQL comments
+COMMENTS = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL)
+COMMENTS2 = re.compile(r'--.*?$', re.MULTILINE)
+
+
+class DatabaseCreation(PostGISCreation):
+ def executescript(self, path, title='SQL'):
+ """
+ Load up a SQL script file and execute.
+ """
+ try:
+ sql = ''.join(open(path).readlines())
+ # strip out comments
+ sql = COMMENTS.sub('', sql)
+ sql = COMMENTS2.sub('', sql)
+ # execute script line by line
+ cursor = self.connection.cursor()
+ self.set_autocommit()
+ for l in re.split(r';', sql):
+ l = l.strip()
+ if len(l) > 0:
+ try:
+ cursor.execute(l)
+ except Exception:
+ message = 'Error running % script: %s' % (title, l)
+ log.exception(message)
+ print >> sys.stderr, message
+ traceback.print_exc()
+ log.info('Executed post setup for %s.', title)
+ except Exception:
+ message = 'Problem in %s script' % (title,)
+ log.exception(message)
+ print >> sys.stderr, message
+ traceback.print_exc()
+
+ def install_hstore_contrib(self):
+ # point to test database
+ self.connection.close()
+ test_database_name = self._get_test_db_name()
+ self.connection.settings_dict["NAME"] = test_database_name
+ # Test to see if HSTORE type was already installed
+ cursor = self.connection.cursor()
+ cursor.execute("SELECT 1 FROM pg_type WHERE typname='hstore';")
+ if cursor.fetchone():
+ # skip if already exists
+ return
+ if self.connection._version[0:2] >= (9, 1):
+ cursor.execute("create extension hstore;")
+ self.connection.commit_unless_managed()
+ return
+ import glob
+ import os
+ # Quick Hack to run HSTORE sql script for test runs
+ sql = getattr(settings, 'HSTORE_SQL', None)
+ if not sql:
+ # Attempt to helpfully locate contrib SQL on typical installs
+ for loc in (
+ # Ubuntu/Debian Location
+ '/usr/share/postgresql/*/contrib/hstore.sql',
+ # Redhat/RPM location
+ '/usr/share/pgsql/contrib/hstore.sql',
+ # MacOSX location
+ '/Library/PostgreSQL/*/share/postgresql/contrib/hstore.sql',
+ # MacPorts location
+ '/opt/local/share/postgresql*/contrib/hstore.sql',
+ # Mac HomeBrew location
+ '/usr/local/Cellar/postgresql/*/share/contrib/hstore.sql',
+ # Windows location
+ 'C:/Program Files/PostgreSQL/*/share/contrib/hstore.sql',
+ # Windows 32-bit location
+ 'C:/Program Files (x86)/PostgreSQL/*/share/contrib/hstore.sql',
+ ):
+ files = glob.glob(loc)
+ if files and len(files) > 0:
+ sql = sorted(files)[-1]
+ log.info("Found installed HSTORE script: %s" % (sql,))
+ break
+ if sql and os.path.isfile(sql):
+ self.executescript(sql, 'HSTORE')
+ else:
+ message = 'Valid HSTORE sql script not found. You may need to install the postgres contrib module.\n' + \
+ 'You can explicitly locate it with the HSTORE_SQL property in django settings.'
+ log.warning(message)
+ print >> sys.stderr, message
+
+ def _create_test_db(self, verbosity, autoclobber):
+ super(DatabaseCreation, self)._create_test_db(verbosity, autoclobber)
+ self.install_hstore_contrib()
+ register_hstore(self.connection.connection, globally=True, unicode=True)
+
+ def sql_indexes_for_field(self, model, f, style):
+ kwargs = VERSION[:2] >= (1, 3) and {'connection': self.connection} or {}
+ if f.db_type(**kwargs) == 'hstore':
+ if not f.db_index:
+ return []
+ # create GIST index for hstore column
+ qn = self.connection.ops.quote_name
+ index_name = '%s_%s_gist' % (model._meta.db_table, f.column)
+ clauses = [style.SQL_KEYWORD('CREATE INDEX'),
+ style.SQL_TABLE(qn(truncate_name(index_name, self.connection.ops.max_name_length()))),
+ style.SQL_KEYWORD('ON'),
+ style.SQL_TABLE(qn(model._meta.db_table)),
+ style.SQL_KEYWORD('USING GIST'),
+ '(%s)' % style.SQL_FIELD(qn(f.column))]
+ # add tablespace clause
+ tablespace = f.db_tablespace or model._meta.db_tablespace
+ if tablespace:
+ sql = self.connection.ops.tablespace_sql(tablespace)
+ if sql:
+ clauses.append(sql)
+ clauses.append(';')
+ return [ ' '.join(clauses) ]
+ return super(DatabaseCreation, self).sql_indexes_for_field(model, f, style)
+
+
+class DatabaseWrapper(DatabaseWrapper):
+ """
+ Custom DB wrapper to inject connection registration and DB creation code
+ """
+
+ def __init__(self, *args, **params):
+ super(DatabaseWrapper, self).__init__(*args, **params)
+ self.creation = DatabaseCreation(self)
+
+ def _cursor(self):
+ # ensure that we're connected
+ cursor = super(DatabaseWrapper, self)._cursor()
+
+ # register hstore extension
+ register_hstore(self.connection, globally=True, unicode=True)
+
+ # bypass future registrations
+ self._cursor = super(DatabaseWrapper, self)._cursor
+ return cursor
@@ -0,0 +1,67 @@
+from django.conf import settings
+from django.db.backends.postgresql_psycopg2.creation import DatabaseCreation
+
+
+class PostGISCreation(DatabaseCreation):
+ geom_index_type = 'GIST'
+ geom_index_opts = 'GIST_GEOMETRY_OPS'
+
+ def sql_indexes_for_field(self, model, f, style):
+ "Return any spatial index creation SQL for the field."
+ from django.contrib.gis.db.models.fields import GeometryField
+
+ output = super(PostGISCreation, self).sql_indexes_for_field(
+ model, f, style)
+
+ if isinstance(f, GeometryField):
+ gqn = self.connection.ops.geo_quote_name
+ qn = self.connection.ops.quote_name
+ db_table = model._meta.db_table
+
+ if f.geography:
+ # Geogrophy columns are created normally.
+ pass
+ else:
+ # Geometry columns are created by `AddGeometryColumn`
+ # stored procedure.
+ output.append(style.SQL_KEYWORD('SELECT ') +
+ style.SQL_TABLE('AddGeometryColumn') + '(' +
+ style.SQL_TABLE(gqn(db_table)) + ', ' +
+ style.SQL_FIELD(gqn(f.column)) + ', ' +
+ style.SQL_FIELD(str(f.srid)) + ', ' +
+ style.SQL_COLTYPE(gqn(f.geom_type)) + ', ' +
+ style.SQL_KEYWORD(str(f.dim)) + ');')
+
+ if not f.null:
+ # Add a NOT NULL constraint to the field
+ output.append(style.SQL_KEYWORD('ALTER TABLE ') +
+ style.SQL_TABLE(qn(db_table)) +
+ style.SQL_KEYWORD(' ALTER ') +
+ style.SQL_FIELD(qn(f.column)) +
+ style.SQL_KEYWORD(' SET NOT NULL') + ';')
+
+ if f.spatial_index:
+ # Spatial indexes created the same way for both Geometry and
+ # Geography columns
+ # PostGIS 2.0 does not support GIST_GEOMETRY_OPS
+ if f.geography:
+ index_opts = ''
+ elif self.connection.ops.spatial_version >= (2, 0):
+ if f.dim > 2:
+ index_opts = ' ' + style.SQL_KEYWORD('gist_geometry_ops_nd')
+ else:
+ index_opts = ''
+ else:
+ index_opts = ' ' + style.SQL_KEYWORD(self.geom_index_opts)
+ output.append(style.SQL_KEYWORD('CREATE INDEX ') +
+ style.SQL_TABLE(qn('%s_%s_id' % (db_table, f.column))) +
+ style.SQL_KEYWORD(' ON ') +
+ style.SQL_TABLE(qn(db_table)) +
+ style.SQL_KEYWORD(' USING ') +
+ style.SQL_COLTYPE(self.geom_index_type) + ' ( ' +
+ style.SQL_FIELD(qn(f.column)) + index_opts + ' );')
+ return output
+
+ def sql_table_creation_suffix(self):
+ qn = self.connection.ops.quote_name
+ return ' TEMPLATE %s' % qn(getattr(settings, 'POSTGIS_TEMPLATE', 'template_postgis'))