Permalink
Browse files

Refactored and cleaned up parts of the spatial database backend. Chan…

…ges include:

* Laid foundations for SpatiaLite support in `GeoQuerySet`, `GeoWhereNode` and the tests.
* Added the `Collect` aggregate for PostGIS (still needs tests).
* Oracle now goes to 11.
* The backend-specific `SpatialRefSys` and `GeometryColumns` models are now attributes of `SpatialBackend`.
* Renamed `GeometryField` attributes to be public that were private (e.g., `_srid` -> `srid` and `_geom` -> `geom_type`).
* Renamed `create_test_db` to `create_test_spatial_db`.
* Removed the legacy classes `GeoMixin` and `GeoQ`.
* Removed evil `\` from spatial backend fields.
* Moved shapefile data from `tests/layermap` to `tests/data`.

Fixed #9794.  Refs #9686.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@10197 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent a61c0b7 commit 867e71501c3273ba4a0b3ac13847cd895f0da663 @jbronn jbronn committed Mar 30, 2009
Showing with 594 additions and 409 deletions.
  1. +5 −3 django/contrib/gis/db/backend/__init__.py
  2. +1 −4 django/contrib/gis/db/backend/base.py
  3. +2 −2 django/contrib/gis/db/backend/mysql/__init__.py
  4. +2 −2 django/contrib/gis/db/backend/mysql/creation.py
  5. +12 −12 django/contrib/gis/db/backend/mysql/field.py
  6. +5 −2 django/contrib/gis/db/backend/oracle/__init__.py
  7. +1 −2 django/contrib/gis/db/backend/oracle/creation.py
  8. +26 −27 django/contrib/gis/db/backend/oracle/field.py
  9. +6 −7 django/contrib/gis/db/backend/oracle/models.py
  10. +6 −2 django/contrib/gis/db/backend/postgis/__init__.py
  11. +5 −22 django/contrib/gis/db/backend/postgis/creation.py
  12. +29 −29 django/contrib/gis/db/backend/postgis/field.py
  13. +4 −9 django/contrib/gis/db/backend/postgis/models.py
  14. +1 −0 django/contrib/gis/db/backend/postgis/query.py
  15. +19 −2 django/contrib/gis/db/backend/util.py
  16. +0 −3 django/contrib/gis/db/models/__init__.py
  17. +4 −1 django/contrib/gis/db/models/aggregates.py
  18. +46 −18 django/contrib/gis/db/models/fields/__init__.py
  19. +0 −11 django/contrib/gis/db/models/mixin.py
  20. +2 −2 django/contrib/gis/db/models/proxy.py
  21. +46 −21 django/contrib/gis/db/models/query.py
  22. +15 −2 django/contrib/gis/db/models/sql/aggregates.py
  23. +4 −0 django/contrib/gis/db/models/sql/conversion.py
  24. +7 −4 django/contrib/gis/db/models/sql/query.py
  25. +5 −5 django/contrib/gis/db/models/sql/where.py
  26. +1 −0 django/contrib/gis/forms/fields.py
  27. +97 −83 django/contrib/gis/models.py
  28. +41 −32 django/contrib/gis/tests/__init__.py
  29. BIN django/contrib/gis/tests/{layermap → data}/cities/cities.dbf
  30. 0 django/contrib/gis/tests/{layermap → data}/cities/cities.prj
  31. BIN django/contrib/gis/tests/{layermap → data}/cities/cities.shp
  32. BIN django/contrib/gis/tests/{layermap → data}/cities/cities.shx
  33. BIN django/contrib/gis/tests/{layermap → data}/counties/counties.dbf
  34. BIN django/contrib/gis/tests/{layermap → data}/counties/counties.shp
  35. BIN django/contrib/gis/tests/{layermap → data}/counties/counties.shx
  36. BIN django/contrib/gis/tests/{layermap → data}/interstates/interstates.dbf
  37. 0 django/contrib/gis/tests/{layermap → data}/interstates/interstates.prj
  38. BIN django/contrib/gis/tests/{layermap → data}/interstates/interstates.shp
  39. BIN django/contrib/gis/tests/{layermap → data}/interstates/interstates.shx
  40. +3 −0 django/contrib/gis/tests/distapp/data.py
  41. +8 −1 django/contrib/gis/tests/distapp/models.py
  42. +70 −36 django/contrib/gis/tests/distapp/tests.py
  43. +11 −10 django/contrib/gis/tests/geoapp/models.py
  44. +84 −33 django/contrib/gis/tests/geoapp/tests.py
  45. +3 −7 django/contrib/gis/tests/layermap/tests.py
  46. +21 −15 django/contrib/gis/tests/relatedapp/tests.py
  47. +2 −0 django/contrib/gis/tests/utils.py
@@ -9,10 +9,12 @@
# Retrieving the necessary settings from the backend.
if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
- from django.contrib.gis.db.backend.postgis import create_spatial_db, get_geo_where_clause, SpatialBackend
+ from django.contrib.gis.db.backend.postgis import create_test_spatial_db, get_geo_where_clause, SpatialBackend
elif settings.DATABASE_ENGINE == 'oracle':
- from django.contrib.gis.db.backend.oracle import create_spatial_db, get_geo_where_clause, SpatialBackend
+ from django.contrib.gis.db.backend.oracle import create_test_spatial_db, get_geo_where_clause, SpatialBackend
elif settings.DATABASE_ENGINE == 'mysql':
- from django.contrib.gis.db.backend.mysql import create_spatial_db, get_geo_where_clause, SpatialBackend
+ from django.contrib.gis.db.backend.mysql import create_test_spatial_db, get_geo_where_clause, SpatialBackend
+elif settings.DATABASE_ENGINE == 'sqlite3':
+ from django.contrib.gis.db.backend.spatialite import create_test_spatial_db, get_geo_where_clause, SpatialBackend
else:
raise NotImplementedError('No Geographic Backend exists for %s' % settings.DATABASE_ENGINE)
@@ -23,7 +23,4 @@ def __getattr__(self, name):
return self.__dict__[name]
except KeyError:
return False
-
-
-
-
+
@@ -1,8 +1,8 @@
-__all__ = ['create_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
+__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
from django.contrib.gis.db.backend.base import BaseSpatialBackend
from django.contrib.gis.db.backend.adaptor import WKTAdaptor
-from django.contrib.gis.db.backend.mysql.creation import create_spatial_db
+from django.contrib.gis.db.backend.mysql.creation import create_test_spatial_db
from django.contrib.gis.db.backend.mysql.field import MySQLGeoField
from django.contrib.gis.db.backend.mysql.query import *
@@ -1,5 +1,5 @@
-def create_spatial_db(test=True, verbosity=1, autoclobber=False):
- if not test: raise NotImplementedError('This uses `create_test_db` from test/utils.py')
+def create_test_spatial_db(verbosity=1, autoclobber=False):
+ "A wrapper over the MySQL `create_test_db` method."
from django.db import connection
connection.creation.create_test_db(verbosity, autoclobber)
@@ -13,20 +13,20 @@ class MySQLGeoField(Field):
def _geom_index(self, style, db_table):
"""
Creates a spatial index for the geometry column. If MyISAM tables are
- used an R-Tree index is created, otherwise a B-Tree index is created.
+ used an R-Tree index is created, otherwise a B-Tree index is created.
Thus, for best spatial performance, you should use MyISAM tables
- (which do not support transactions). For more information, see Ch.
+ (which do not support transactions). For more information, see Ch.
16.6.1 of the MySQL 5.0 documentation.
"""
# Getting the index name.
idx_name = '%s_%s_id' % (db_table, self.column)
-
- sql = style.SQL_KEYWORD('CREATE SPATIAL INDEX ') + \
- style.SQL_TABLE(qn(idx_name)) + \
- style.SQL_KEYWORD(' ON ') + \
- style.SQL_TABLE(qn(db_table)) + '(' + \
- style.SQL_FIELD(qn(self.column)) + ');'
+
+ sql = (style.SQL_KEYWORD('CREATE SPATIAL INDEX ') +
+ style.SQL_TABLE(qn(idx_name)) +
+ style.SQL_KEYWORD(' ON ') +
+ style.SQL_TABLE(qn(db_table)) + '(' +
+ style.SQL_FIELD(qn(self.column)) + ');')
return sql
def post_create_sql(self, style, db_table):
@@ -35,19 +35,19 @@ def post_create_sql(self, style, db_table):
created.
"""
# Getting the geometric index for this Geometry column.
- if self._index:
+ if self.spatial_index:
return (self._geom_index(style, db_table),)
else:
return ()
def db_type(self):
"The OpenGIS name is returned for the MySQL database column type."
- return self._geom
+ return self.geom_type
def get_placeholder(self, value):
"""
- The placeholder here has to include MySQL's WKT constructor. Because
- MySQL does not support spatial transformations, there is no need to
+ The placeholder here has to include MySQL's WKT constructor. Because
+ MySQL does not support spatial transformations, there is no need to
modify the placeholder based on the contents of the given value.
"""
return '%s(%%s)' % GEOM_FROM_TEXT
@@ -1,9 +1,10 @@
-__all__ = ['create_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
+__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
from django.contrib.gis.db.backend.base import BaseSpatialBackend
from django.contrib.gis.db.backend.oracle.adaptor import OracleSpatialAdaptor
-from django.contrib.gis.db.backend.oracle.creation import create_spatial_db
+from django.contrib.gis.db.backend.oracle.creation import create_test_spatial_db
from django.contrib.gis.db.backend.oracle.field import OracleSpatialField
+from django.contrib.gis.db.backend.oracle.models import GeometryColumns, SpatialRefSys
from django.contrib.gis.db.backend.oracle.query import *
SpatialBackend = BaseSpatialBackend(name='oracle', oracle=True,
@@ -29,4 +30,6 @@
union=UNION,
Adaptor=OracleSpatialAdaptor,
Field=OracleSpatialField,
+ GeometryColumns=GeometryColumns,
+ SpatialRefSys=SpatialRefSys,
)
@@ -1,6 +1,5 @@
-def create_spatial_db(test=True, verbosity=1, autoclobber=False):
+def create_test_spatial_db(verbosity=1, autoclobber=False):
"A wrapper over the Oracle `create_test_db` routine."
- if not test: raise NotImplementedError('This uses `create_test_db` from db/backends/oracle/creation.py')
from django.db import connection
connection.creation.create_test_db(verbosity, autoclobber)
@@ -32,26 +32,25 @@ def _add_geom(self, style, db_table):
Adds this geometry column into the Oracle USER_SDO_GEOM_METADATA
table.
"""
-
# Checking the dimensions.
# TODO: Add support for 3D geometries.
- if self._dim != 2:
+ if self.dim != 2:
raise Exception('3D geometries not yet supported on Oracle Spatial backend.')
# Constructing the SQL that will be used to insert information about
# the geometry column into the USER_GSDO_GEOM_METADATA table.
- meta_sql = style.SQL_KEYWORD('INSERT INTO ') + \
- style.SQL_TABLE('USER_SDO_GEOM_METADATA') + \
- ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) + \
- style.SQL_KEYWORD(' VALUES ') + '(\n ' + \
- style.SQL_TABLE(gqn(db_table)) + ',\n ' + \
- style.SQL_FIELD(gqn(self.column)) + ',\n ' + \
- style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' + \
- style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
- ("('LONG', %s, %s, %s),\n " % (self._extent[0], self._extent[2], self._tolerance)) + \
- style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") + \
- ("('LAT', %s, %s, %s)\n ),\n" % (self._extent[1], self._extent[3], self._tolerance)) + \
- ' %s\n );' % self._srid
+ meta_sql = (style.SQL_KEYWORD('INSERT INTO ') +
+ style.SQL_TABLE('USER_SDO_GEOM_METADATA') +
+ ' (%s, %s, %s, %s)\n ' % tuple(map(qn, ['TABLE_NAME', 'COLUMN_NAME', 'DIMINFO', 'SRID'])) +
+ style.SQL_KEYWORD(' VALUES ') + '(\n ' +
+ style.SQL_TABLE(gqn(db_table)) + ',\n ' +
+ style.SQL_FIELD(gqn(self.column)) + ',\n ' +
+ style.SQL_KEYWORD("MDSYS.SDO_DIM_ARRAY") + '(\n ' +
+ style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
+ ("('LONG', %s, %s, %s),\n " % (self._extent[0], self._extent[2], self._tolerance)) +
+ style.SQL_KEYWORD("MDSYS.SDO_DIM_ELEMENT") +
+ ("('LAT', %s, %s, %s)\n ),\n" % (self._extent[1], self._extent[3], self._tolerance)) +
+ ' %s\n );' % self.srid)
return meta_sql
def _geom_index(self, style, db_table):
@@ -60,14 +59,14 @@ def _geom_index(self, style, db_table):
# Getting the index name, Oracle doesn't allow object
# names > 30 characters.
idx_name = truncate_name('%s_%s_id' % (db_table, self.column), 30)
-
- sql = style.SQL_KEYWORD('CREATE INDEX ') + \
- style.SQL_TABLE(qn(idx_name)) + \
- style.SQL_KEYWORD(' ON ') + \
- style.SQL_TABLE(qn(db_table)) + '(' + \
- style.SQL_FIELD(qn(self.column)) + ') ' + \
- style.SQL_KEYWORD('INDEXTYPE IS ') + \
- style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';'
+
+ sql = (style.SQL_KEYWORD('CREATE INDEX ') +
+ style.SQL_TABLE(qn(idx_name)) +
+ style.SQL_KEYWORD(' ON ') +
+ style.SQL_TABLE(qn(db_table)) + '(' +
+ style.SQL_FIELD(qn(self.column)) + ') ' +
+ style.SQL_KEYWORD('INDEXTYPE IS ') +
+ style.SQL_TABLE('MDSYS.SPATIAL_INDEX') + ';')
return sql
def post_create_sql(self, style, db_table):
@@ -79,15 +78,15 @@ def post_create_sql(self, style, db_table):
post_sql = self._add_geom(style, db_table)
# Getting the geometric index for this Geometry column.
- if self._index:
+ if self.spatial_index:
return (post_sql, self._geom_index(style, db_table))
else:
return (post_sql,)
def db_type(self):
"The Oracle geometric data type is MDSYS.SDO_GEOMETRY."
return 'MDSYS.SDO_GEOMETRY'
-
+
def get_placeholder(self, value):
"""
Provides a proper substitution value for Geometries that are not in the
@@ -96,8 +95,8 @@ def get_placeholder(self, value):
"""
if value is None:
return '%s'
- elif value.srid != self._srid:
+ elif value.srid != self.srid:
# Adding Transform() to the SQL placeholder.
- return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self._srid)
+ return '%s(SDO_GEOMETRY(%%s, %s), %s)' % (TRANSFORM, value.srid, self.srid)
else:
- return 'SDO_GEOMETRY(%%s, %s)' % self._srid
+ return 'SDO_GEOMETRY(%%s, %s)' % self.srid
@@ -8,7 +8,6 @@
model and the `SDO_COORD_REF_SYS` is used for the SpatialRefSys model.
"""
from django.db import models
-from django.contrib.gis.models import SpatialRefSysMixin
class GeometryColumns(models.Model):
"Maps to the Oracle USER_SDO_GEOM_METADATA table."
@@ -22,35 +21,35 @@ class Meta:
@classmethod
def table_name_col(cls):
"""
- Returns the name of the metadata column used to store the
+ Returns the name of the metadata column used to store the
the feature table name.
"""
return 'table_name'
@classmethod
def geom_col_name(cls):
"""
- Returns the name of the metadata column used to store the
+ Returns the name of the metadata column used to store the
the feature geometry column.
"""
return 'column_name'
def __unicode__(self):
return '%s - %s (SRID: %s)' % (self.table_name, self.column_name, self.srid)
-class SpatialRefSys(models.Model, SpatialRefSysMixin):
+class SpatialRefSys(models.Model):
"Maps to the Oracle MDSYS.CS_SRS table."
cs_name = models.CharField(max_length=68)
srid = models.IntegerField(primary_key=True)
auth_srid = models.IntegerField()
auth_name = models.CharField(max_length=256)
wktext = models.CharField(max_length=2046)
- #cs_bounds = models.GeometryField()
+ #cs_bounds = models.GeometryField() # TODO
class Meta:
- # TODO: Figure out way to have this be MDSYS.CS_SRS without
- # having django's quoting mess up the SQL.
+ abstract = True
db_table = 'CS_SRS'
+ app_label = '_mdsys' # Hack so that syncdb won't try to create "CS_SRS" table.
@property
def wkt(self):
@@ -1,14 +1,16 @@
-__all__ = ['create_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
+__all__ = ['create_test_spatial_db', 'get_geo_where_clause', 'SpatialBackend']
from django.contrib.gis.db.backend.base import BaseSpatialBackend
from django.contrib.gis.db.backend.postgis.adaptor import PostGISAdaptor
-from django.contrib.gis.db.backend.postgis.creation import create_spatial_db
+from django.contrib.gis.db.backend.postgis.creation import create_test_spatial_db
from django.contrib.gis.db.backend.postgis.field import PostGISField
+from django.contrib.gis.db.backend.postgis.models import GeometryColumns, SpatialRefSys
from django.contrib.gis.db.backend.postgis.query import *
SpatialBackend = BaseSpatialBackend(name='postgis', postgis=True,
area=AREA,
centroid=CENTROID,
+ collect=COLLECT,
difference=DIFFERENCE,
distance=DISTANCE,
distance_functions=DISTANCE_FUNCTIONS,
@@ -39,4 +41,6 @@
version=(MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2),
Adaptor=PostGISAdaptor,
Field=PostGISField,
+ GeometryColumns=GeometryColumns,
+ SpatialRefSys=SpatialRefSys,
)
@@ -1,23 +1,10 @@
import os, re, sys
-from subprocess import Popen, PIPE
from django.conf import settings
from django.core.management import call_command
from django.db import connection
from django.db.backends.creation import TEST_DATABASE_PREFIX
-
-def getstatusoutput(cmd):
- """
- Executes a shell command on the platform using subprocess.Popen and
- return a tuple of the status and stdout output.
- """
- # Set stdout and stderr to PIPE because we want to capture stdout and
- # prevent stderr from displaying.
- p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
- # We use p.communicate() instead of p.wait() to avoid deadlocks if the
- # output buffers exceed POSIX buffer size.
- stdout, stderr = p.communicate()
- return p.returncode, stdout.strip()
+from django.contrib.gis.db.backend.util import getstatusoutput
def create_lang(db_name, verbosity=1):
"Sets up the pl/pgsql language on the given database."
@@ -110,20 +97,16 @@ def _create_with_shell(db_name, verbosity=1, autoclobber=False):
else:
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."
+def create_test_spatial_db(verbosity=1, autoclobber=False, interactive=False):
+ "Creates a test 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.')
# Getting the spatial database name
- if test:
- db_name = get_spatial_db(test=True)
- _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
- else:
- db_name = get_spatial_db()
- _create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber)
+ db_name = get_spatial_db(test=True)
+ _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
# If a template database is used, then don't need to do any of the following.
if not hasattr(settings, 'POSTGIS_TEMPLATE'):
Oops, something went wrong.

0 comments on commit 867e715

Please sign in to comment.