Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for postgis #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
147 changes: 147 additions & 0 deletions django_hstore/backends/postgis/base.py
Original file line number Diff line number Diff line change
@@ -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
67 changes: 67 additions & 0 deletions django_hstore/backends/postgis/creation.py
Original file line number Diff line number Diff line change
@@ -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'))
Empty file.