summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2007-09-30 04:19:31 +0000
committerJustin Bronn <jbronn@gmail.com>2007-09-30 04:19:31 +0000
commit867a74495f33b0434c796ef94efdb3093fe101db (patch)
tree460c6f8ef0c00b52f6afde031351ad0af90034e5
parent53a0514e2bfcf2fa595f4267c84f4acffd6cc520 (diff)
gis: PostGIS backend improvements: test spatial databases may now be created on NT platforms; changed exception style.
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6439 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/contrib/gis/db/backend/__init__.py12
-rw-r--r--django/contrib/gis/db/backend/postgis/__init__.py9
-rw-r--r--django/contrib/gis/db/backend/postgis/creation.py91
-rw-r--r--django/contrib/gis/db/backend/postgis/field.py11
-rw-r--r--django/contrib/gis/db/backend/postgis/management.py8
-rw-r--r--django/contrib/gis/db/backend/postgis/query.py8
6 files changed, 82 insertions, 57 deletions
diff --git a/django/contrib/gis/db/backend/__init__.py b/django/contrib/gis/db/backend/__init__.py
index 50f7fbc34c..651a1976c5 100644
--- a/django/contrib/gis/db/backend/__init__.py
+++ b/django/contrib/gis/db/backend/__init__.py
@@ -8,8 +8,8 @@
(2) The parse_lookup() function, used for spatial SQL construction by
the GeoQuerySet.
- Currently only PostGIS is supported, but someday backends will be aded for
- additional spatial databases.
+ Currently only PostGIS is supported, but someday backends will be added for
+ additional spatial databases (e.g., Oracle, DB2).
"""
from django.conf import settings
from django.db import connection
@@ -20,11 +20,11 @@ from django.utils.datastructures import SortedDict
if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
# PostGIS is the spatial database, getting the rquired modules, renaming as necessary.
from django.contrib.gis.db.backend.postgis import \
- PostGISField as GeoBackendField, \
- POSTGIS_TERMS as GIS_TERMS, \
- create_spatial_db, geo_quotename, get_geo_where_clause
+ PostGISField as GeoBackendField, POSTGIS_TERMS as GIS_TERMS, \
+ create_spatial_db, geo_quotename, get_geo_where_clause, \
+ ASGML, ASKML, UNION
else:
- raise NotImplementedError, 'No Geographic Backend exists for %s' % settings.DATABASE_NAME
+ raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_NAME)
#### query.py overloaded functions ####
# parse_lookup() and lookup_inner() are modified from their django/db/models/query.py
diff --git a/django/contrib/gis/db/backend/postgis/__init__.py b/django/contrib/gis/db/backend/postgis/__init__.py
index 5f2e8f06a1..33ef9a43fa 100644
--- a/django/contrib/gis/db/backend/postgis/__init__.py
+++ b/django/contrib/gis/db/backend/postgis/__init__.py
@@ -8,11 +8,16 @@ from django.contrib.gis.db.backend.postgis.query import \
from django.contrib.gis.db.backend.postgis.creation import create_spatial_db
from django.contrib.gis.db.backend.postgis.field import PostGISField
-# Whether PostGIS has AsKML() support.
+# Functions used by GeoManager methods, and not via lookup types.
if MAJOR_VERSION == 1:
- # AsKML() only supported in versions 1.2.1+
if MINOR_VERSION1 == 3:
ASKML = 'ST_AsKML'
+ ASGML = 'ST_AsGML'
+ UNION = 'ST_Union'
elif MINOR_VERSION1 == 2 and MINOR_VERSION2 >= 1:
ASKML = 'AsKML'
+ ASGML = 'AsGML'
+ UNION = 'GeomUnion'
+
+
diff --git a/django/contrib/gis/db/backend/postgis/creation.py b/django/contrib/gis/db/backend/postgis/creation.py
index 1d0c60010d..994edb10e7 100644
--- a/django/contrib/gis/db/backend/postgis/creation.py
+++ b/django/contrib/gis/db/backend/postgis/creation.py
@@ -2,9 +2,16 @@ from django.conf import settings
from django.core.management import call_command
from django.db import connection
from django.test.utils import _set_autocommit, TEST_DATABASE_PREFIX
-from commands import getstatusoutput
import os, re, sys
+def getstatusoutput(cmd):
+ "A simpler version of getstatusoutput that works on win32 platforms."
+ stdin, stdout, stderr = os.popen3(cmd)
+ output = stdout.read()
+ if output.endswith('\n'): output = output[:-1]
+ status = stdin.close()
+ return status, output
+
def create_lang(db_name, verbosity=1):
"Sets up the pl/pgsql language on the given database."
@@ -20,8 +27,8 @@ def create_lang(db_name, verbosity=1):
status, output = getstatusoutput(createlang_cmd)
# Checking the status of the command, 0 => execution successful
- if status != 0:
- raise Exception, "Error executing 'plpgsql' command: %s\n" % output
+ if status:
+ raise Exception("Error executing 'plpgsql' command: %s\n" % output)
def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
"Creates database with psycopg2 cursor."
@@ -48,7 +55,7 @@ def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
if verbosity >= 1: print 'Creating new spatial database...'
cursor.execute(create_sql)
else:
- raise Exception, 'Spatial Database Creation canceled.'
+ raise Exception('Spatial Database Creation canceled.')
created_regex = re.compile(r'^createdb: database creation failed: ERROR: database ".+" already exists')
def _create_with_shell(db_name, verbosity=1, autoclobber=False):
@@ -65,7 +72,8 @@ def _create_with_shell(db_name, verbosity=1, autoclobber=False):
# Attempting to create the database.
status, output = getstatusoutput(create_cmd)
- if status != 0:
+
+ if status:
if created_regex.match(output):
if not autoclobber:
confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
@@ -74,27 +82,22 @@ def _create_with_shell(db_name, verbosity=1, autoclobber=False):
drop_cmd = 'dropdb %s%s' % (options, db_name)
status, output = getstatusoutput(drop_cmd)
if status != 0:
- raise Exception, 'Could not drop database %s: %s' % (db_name, output)
+ raise Exception('Could not drop database %s: %s' % (db_name, output))
if verbosity >= 1: print 'Creating new spatial database...'
status, output = getstatusoutput(create_cmd)
if status != 0:
- raise Exception, 'Could not create database after dropping: %s' % output
+ raise Exception('Could not create database after dropping: %s' % output)
else:
- raise Exception, 'Spatial Database Creation canceled.'
+ raise Exception('Spatial Database Creation canceled.')
else:
- raise Exception, 'Unknown error occurred in creating database: %s' % output
+ raise Exception('Unknown error occurred in creating database: %s' % output)
def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=False):
"Creates a spatial database based on the settings."
# Making sure we're using PostgreSQL and psycopg2
if settings.DATABASE_ENGINE != 'postgresql_psycopg2':
- raise Exception, 'Spatial database creation only supported postgresql_psycopg2 platform.'
-
- # This routine depends on getstatusoutput(), which does not work on Windows.
- # TODO: Consider executing shell commands with popen for Windows compatibility
- if os.name == 'nt':
- raise Exception, 'Automatic spatial database creation only supported on *NIX platforms.'
+ raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.')
# Getting the spatial database name
if test:
@@ -104,8 +107,10 @@ def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=Fa
db_name = get_spatial_db()
_create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber)
- # Creating the db language.
- create_lang(db_name, verbosity=verbosity)
+ # Creating the db language, does not need to be done on NT platforms
+ # since the PostGIS installer enables this capability.
+ if os.name != 'nt':
+ create_lang(db_name, verbosity=verbosity)
# Now adding in the PostGIS routines.
load_postgis_sql(db_name, verbosity=verbosity)
@@ -118,19 +123,15 @@ def create_spatial_db(test=False, verbosity=1, autoclobber=False, interactive=Fa
# Syncing the database
call_command('syncdb', verbosity=verbosity, interactive=interactive)
-
- # Get a cursor (even though we don't need one yet). This has
- # the side effect of initializing the test database.
- cursor = connection.cursor()
def drop_db(db_name=False, test=False):
"""
- Drops the given database (defaults to what is returned from get_spatial_db().
- All exceptions are propagated up to the caller.
+ Drops the given database (defaults to what is returned from
+ get_spatial_db()). All exceptions are propagated up to the caller.
"""
if not db_name: db_name = get_spatial_db(test=test)
cursor = connection.cursor()
- cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(db_name))
+ cursor.execute('DROP DATABASE %s' % connection.ops.quote_name(db_name))
def get_cmd_options(db_name):
"Obtains the command-line PostgreSQL connection options for shell commands."
@@ -159,7 +160,7 @@ def get_spatial_db(test=False):
return test_db_name
else:
if not settings.DATABASE_NAME:
- raise Exception, 'must configure DATABASE_NAME in settings.py'
+ raise Exception('must configure DATABASE_NAME in settings.py')
return settings.DATABASE_NAME
def load_postgis_sql(db_name, verbosity=1):
@@ -171,35 +172,51 @@ def load_postgis_sql(db_name, verbosity=1):
# Getting the path to the PostGIS SQL
try:
# POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
- # PostGIS SQL files are located
+ # PostGIS SQL files are located. This is especially useful on Win32
+ # platforms since the output of pg_config looks like "C:/PROGRA~1/..".
sql_path = settings.POSTGIS_SQL_PATH
except AttributeError:
status, sql_path = getstatusoutput('pg_config --sharedir')
- if status != 0:
+ if status:
sql_path = '/usr/local/share'
# The PostGIS SQL post-creation files.
lwpostgis_file = os.path.join(sql_path, 'lwpostgis.sql')
- srefsys_file = os.path.join(sql_path, 'spatial_ref_sys.sql')
+ srefsys_file = os.path.join(sql_path, 'spatial_ref_sys.sql')
if not os.path.isfile(lwpostgis_file):
- raise Exception, 'Could not find PostGIS function definitions in %s' % lwpostgis_file
+ raise Exception('Could not find PostGIS function definitions in %s' % lwpostgis_file)
if not os.path.isfile(srefsys_file):
- raise Exception, 'Could not find PostGIS spatial reference system definitions in %s' % srefsys_file
+ raise Exception('Could not find PostGIS spatial reference system definitions in %s' % srefsys_file)
- # Getting the psql command-line options.
+ # Getting the psql command-line options, and command format.
options = get_cmd_options(db_name)
+ cmd_fmt = 'psql %s-f "%%s"' % options
# Now trying to load up the PostGIS functions
- cmd = 'psql %s-f %s' % (options, lwpostgis_file)
+ cmd = cmd_fmt % lwpostgis_file
if verbosity >= 1: print cmd
status, output = getstatusoutput(cmd)
- if status != 0:
- raise Exception, 'Error in loading PostGIS lwgeometry routines.'
+ if status:
+ raise Exception('Error in loading PostGIS lwgeometry routines.')
# Now trying to load up the Spatial Reference System table
- cmd = 'psql %s-f %s' % (options, srefsys_file)
+ cmd = cmd_fmt % srefsys_file
if verbosity >= 1: print cmd
status, output = getstatusoutput(cmd)
- if status !=0:
- raise Exception, 'Error in loading PostGIS spatial_ref_sys table.'
+ if status:
+ raise Exception('Error in loading PostGIS spatial_ref_sys table.')
+
+ # Setting the permissions because on Windows platforms the owner
+ # of the spatial_ref_sys and geometry_columns tables is always
+ # the postgres user, regardless of how the db is created.
+ if os.name == 'nt': set_permissions(db_name)
+def set_permissions(db_name):
+ """
+ Sets the permissions on the given database to that of the user specified
+ in the settings. Needed specifically for PostGIS on Win32 platforms.
+ """
+ cursor = connection.cursor()
+ user = settings.DATABASE_USER
+ cursor.execute('ALTER TABLE geometry_columns OWNER TO %s' % user)
+ cursor.execute('ALTER TABLE spatial_ref_sys OWNER TO %s' % user)
diff --git a/django/contrib/gis/db/backend/postgis/field.py b/django/contrib/gis/db/backend/postgis/field.py
index 2f2834f31f..c1cfc5e97e 100644
--- a/django/contrib/gis/db/backend/postgis/field.py
+++ b/django/contrib/gis/db/backend/postgis/field.py
@@ -5,7 +5,8 @@ from types import StringType
class PostGISField(Field):
def _add_geom(self, style, db_table):
- """Constructs the addition of the geometry to the table using the
+ """
+ Constructs the addition of the geometry to the table using the
AddGeometryColumn(...) PostGIS (and OGC standard) stored procedure.
Takes the style object (provides syntax highlighting) and the
@@ -98,10 +99,14 @@ class PostGISField(Field):
if isinstance(value, GEOSGeometry):
return value
else:
- return ("SRID=%d;%s" % (self._srid, wkt))
+ raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')
def get_placeholder(self, value):
- "Provides a proper substitution value for "
+ """
+ Provides a proper substitution value for Geometries that are not in the
+ SRID of the field. Specifically, this routine will substitute in the
+ ST_Transform() function call.
+ """
if isinstance(value, GEOSGeometry) and value.srid != self._srid:
# Adding Transform() to the SQL placeholder.
return 'ST_Transform(%%s, %s)' % self._srid
diff --git a/django/contrib/gis/db/backend/postgis/management.py b/django/contrib/gis/db/backend/postgis/management.py
index e323d83f7d..c1cb32a04f 100644
--- a/django/contrib/gis/db/backend/postgis/management.py
+++ b/django/contrib/gis/db/backend/postgis/management.py
@@ -1,5 +1,6 @@
"""
- This utility module is for obtaining information about the PostGIS installation.
+ This utility module is for obtaining information about the PostGIS
+ installation.
See PostGIS docs at Ch. 6.2.1 for more information on these functions.
"""
@@ -48,9 +49,6 @@ def postgis_version_tuple():
minor1 = int(m.group('minor1'))
minor2 = int(m.group('minor2'))
else:
- raise Exception, 'Could not parse PostGIS version string: %s' % version
+ raise Exception('Could not parse PostGIS version string: %s' % version)
return (version, major, minor1, minor2)
-
-
-
diff --git a/django/contrib/gis/db/backend/postgis/query.py b/django/contrib/gis/db/backend/postgis/query.py
index 4979e5b1c9..d8b13eb37f 100644
--- a/django/contrib/gis/db/backend/postgis/query.py
+++ b/django/contrib/gis/db/backend/postgis/query.py
@@ -14,7 +14,7 @@ POSTGIS_VERSION, MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2 = postgis_version
# TODO: Confirm tests with PostGIS versions 1.1.x -- should work.
# Versions <= 1.0.x do not use GEOS C API, and will not be supported.
if MAJOR_VERSION != 1 or (MAJOR_VERSION == 1 and MINOR_VERSION1 < 1):
- raise Exception, 'PostGIS version %s not supported.' % POSTGIS_VERSION
+ raise Exception('PostGIS version %s not supported.' % POSTGIS_VERSION)
# PostGIS-specific operators. The commented descriptions of these
# operators come from Section 6.2.2 of the official PostGIS documentation.
@@ -145,11 +145,11 @@ def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
# Ensuring that a tuple _value_ was passed in from the user
if not isinstance(value, tuple) or len(value) != 2:
- raise TypeError, '2-element tuple required for %s lookup type.' % lookup_type
+ raise TypeError('2-element tuple required for %s lookup type.' % lookup_type)
# Ensuring the argument type matches what we expect.
if not isinstance(value[1], arg_type):
- raise TypeError, 'Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))
+ raise TypeError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1])))
return "%s(%s%s, %%s, %%s)" % (func, table_prefix, field_name)
else:
@@ -161,7 +161,7 @@ def get_geo_where_clause(lookup_type, table_prefix, field_name, value):
if lookup_type == 'isnull':
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
- raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type)
+ raise TypeError("Got invalid lookup_type: %s" % repr(lookup_type))
def geo_quotename(value, dbl=False):
"Returns the quotation used for PostGIS on a given value (uses single quotes by default)."