summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/gis/db/backend/__init__.py28
-rw-r--r--django/contrib/gis/db/backend/postgis/__init__.py1
-rw-r--r--django/contrib/gis/db/backend/postgis/field.py29
-rw-r--r--django/contrib/gis/db/backend/postgis/models.py54
-rw-r--r--django/contrib/gis/db/backend/postgis/proxy.py62
-rw-r--r--django/contrib/gis/db/models/fields/__init__.py29
-rw-r--r--django/contrib/gis/db/models/mixin.py59
-rw-r--r--django/contrib/gis/db/models/proxy.py47
-rw-r--r--django/contrib/gis/models.py80
-rw-r--r--django/contrib/gis/tests/geoapp/models.py17
-rw-r--r--django/contrib/gis/tests/geoapp/tests.py42
-rw-r--r--django/core/management/sql.py2
12 files changed, 251 insertions, 199 deletions
diff --git a/django/contrib/gis/db/backend/__init__.py b/django/contrib/gis/db/backend/__init__.py
index f3e4e8ecdc..a28a8d1cff 100644
--- a/django/contrib/gis/db/backend/__init__.py
+++ b/django/contrib/gis/db/backend/__init__.py
@@ -1,15 +1,21 @@
"""
- This module provides the backend for spatial SQL construction with Django.
+ This module provides the backend for spatial SQL construction with Django.
- Specifically, this module will import the correct routines and modules
- needed for GeoDjango
+ Specifically, this module will import the correct routines and modules
+ needed for GeoDjango.
+
+ (1) GeoBackEndField, a base class needed for GeometryField.
+ (2) GeometryProxy, for lazy-instantiated geometries from the
+ database output.
+ (3) GIS_TERMS, a list of acceptable geographic lookup types for
+ the backend.
+ (4) The `parse_lookup` function, used for spatial SQL construction by
+ the GeoQuerySet.
+ (5) The `create_spatial_db`, `geo_quotename`, and `get_geo_where_clause`
+ routines (needed by `parse_lookup`.
- (1) GeoBackEndField, a base class needed for GeometryField.
- (2) The parse_lookup() function, used for spatial SQL construction by
- the GeoQuerySet.
-
- Currently only PostGIS is supported, but someday backends will be added for
- additional spatial databases (e.g., Oracle, DB2).
+ 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
@@ -21,9 +27,11 @@ from django.utils.datastructures import SortedDict
ASGML, ASKML, UNION = (False, False, False)
if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
- # PostGIS is the spatial database, getting the rquired modules, renaming as necessary.
+ # 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, \
+ PostGISProxy as GeometryProxy, \
create_spatial_db, geo_quotename, get_geo_where_clause, \
ASGML, ASKML, UNION
else:
diff --git a/django/contrib/gis/db/backend/postgis/__init__.py b/django/contrib/gis/db/backend/postgis/__init__.py
index 33ef9a43fa..d95bde4370 100644
--- a/django/contrib/gis/db/backend/postgis/__init__.py
+++ b/django/contrib/gis/db/backend/postgis/__init__.py
@@ -7,6 +7,7 @@ from django.contrib.gis.db.backend.postgis.query import \
MAJOR_VERSION, MINOR_VERSION1, MINOR_VERSION2
from django.contrib.gis.db.backend.postgis.creation import create_spatial_db
from django.contrib.gis.db.backend.postgis.field import PostGISField
+from django.contrib.gis.db.backend.postgis.proxy import PostGISProxy
# Functions used by GeoManager methods, and not via lookup types.
if MAJOR_VERSION == 1:
diff --git a/django/contrib/gis/db/backend/postgis/field.py b/django/contrib/gis/db/backend/postgis/field.py
index c1cfc5e97e..2d941dfd0b 100644
--- a/django/contrib/gis/db/backend/postgis/field.py
+++ b/django/contrib/gis/db/backend/postgis/field.py
@@ -44,9 +44,11 @@ class PostGISField(Field):
return sql
def _post_create_sql(self, style, db_table):
- """Returns SQL that will be executed after the model has been
+ """
+ Returns SQL that will be executed after the model has been
created. Geometry columns must be added after creation with the
- PostGIS AddGeometryColumn() function."""
+ PostGIS AddGeometryColumn() function.
+ """
# Getting the AddGeometryColumn() SQL necessary to create a PostGIS
# geometry field.
@@ -54,9 +56,9 @@ class PostGISField(Field):
# If the user wants to index this data, then get the indexing SQL as well.
if self._index:
- return '%s\n%s' % (post_sql, self._geom_index(style, db_table))
+ return (post_sql, self._geom_index(style, db_table))
else:
- return post_sql
+ return (post_sql,)
def _post_delete_sql(self, style, db_table):
"Drops the geometry column."
@@ -66,9 +68,18 @@ class PostGISField(Field):
style.SQL_FIELD(quotename(self.column)) + ');'
return sql
+ def db_type(self):
+ """
+ PostGIS geometry columns are added by stored procedures, should be
+ None.
+ """
+ return None
+
def get_db_prep_lookup(self, lookup_type, value):
- """Returns field's value prepared for database lookup, accepts WKT and
- GEOS Geometries for the value."""
+ """
+ Returns field's value prepared for database lookup, accepts WKT and
+ GEOS Geometries for the value.
+ """
if lookup_type in POSTGIS_TERMS:
if lookup_type == 'isnull': return [value] # special case for NULL geometries.
if not bool(value): return [None] # If invalid value passed in.
@@ -101,6 +112,12 @@ class PostGISField(Field):
else:
raise TypeError('Geometry Proxy should only return GEOSGeometry objects.')
+ def get_internal_type(self):
+ """
+ Returns NoField because a stored procedure is used by PostGIS to create the
+ """
+ return 'NoField'
+
def get_placeholder(self, value):
"""
Provides a proper substitution value for Geometries that are not in the
diff --git a/django/contrib/gis/db/backend/postgis/models.py b/django/contrib/gis/db/backend/postgis/models.py
new file mode 100644
index 0000000000..4071dc646b
--- /dev/null
+++ b/django/contrib/gis/db/backend/postgis/models.py
@@ -0,0 +1,54 @@
+"""
+ The GeometryColumns and SpatialRefSys models for the PostGIS backend.
+"""
+from django.db import models
+from django.contrib.gis.models import SpatialRefSysMixin
+
+# Checking for the presence of GDAL (needed for the SpatialReference object)
+from django.contrib.gis.gdal import HAS_GDAL
+if HAS_GDAL:
+ from django.contrib.gis.gdal import SpatialReference
+
+class GeometryColumns(models.Model):
+ """
+ The 'geometry_columns' table from the PostGIS. See the PostGIS
+ documentation at Ch. 4.2.2.
+ """
+ f_table_catalog = models.CharField(maxlength=256)
+ f_table_schema = models.CharField(maxlength=256)
+ f_table_name = models.CharField(maxlength=256, primary_key=True)
+ f_geometry_column = models.CharField(maxlength=256)
+ coord_dimension = models.IntegerField()
+ srid = models.IntegerField()
+ type = models.CharField(maxlength=30)
+
+ class Meta:
+ db_table = 'geometry_columns'
+
+ @classmethod
+ def table_name(self):
+ "Class method for returning the table name field for this model."
+ return 'f_table_name'
+
+ def __unicode__(self):
+ return "%s.%s - %dD %s field (SRID: %d)" % \
+ (self.f_table_name, self.f_geometry_column,
+ self.coord_dimension, self.type, self.srid)
+
+class SpatialRefSys(models.Model, SpatialRefSysMixin):
+ """
+ The 'spatial_ref_sys' table from PostGIS. See the PostGIS
+ documentaiton at Ch. 4.2.1.
+ """
+ srid = models.IntegerField(primary_key=True)
+ auth_name = models.CharField(maxlength=256)
+ auth_srid = models.IntegerField()
+ srtext = models.CharField(maxlength=2048)
+ proj4text = models.CharField(maxlength=2048)
+
+ class Meta:
+ db_table = 'spatial_ref_sys'
+
+ @property
+ def wkt(self):
+ return self.srtext
diff --git a/django/contrib/gis/db/backend/postgis/proxy.py b/django/contrib/gis/db/backend/postgis/proxy.py
new file mode 100644
index 0000000000..bb1ee7f280
--- /dev/null
+++ b/django/contrib/gis/db/backend/postgis/proxy.py
@@ -0,0 +1,62 @@
+"""
+ The GeometryProxy object, allows for lazy-geometries. The proxy uses
+ Python descriptors for instantiating and setting GEOS Geometry objects
+ corresponding to geographic model fields.
+
+ Thanks to Robert Coup for providing this functionality (see #4322).
+"""
+
+from types import NoneType, StringType, UnicodeType
+
+class PostGISProxy(object):
+ def __init__(self, klass, field):
+ """
+ Proxy initializes on the given Geometry class (not an instance) and
+ the GeometryField.
+ """
+ self._field = field
+ self._klass = klass
+
+ def __get__(self, obj, type=None):
+ """
+ This accessor retrieves the geometry, initializing it using the geometry
+ class specified during initialization and the HEXEWKB value of the field.
+ Currently, only GEOS or OGR geometries are supported.
+ """
+ # Getting the value of the field.
+ geom_value = obj.__dict__[self._field.attname]
+
+ if isinstance(geom_value, self._klass):
+ geom = geom_value
+ elif (geom_value is None) or (geom_value==''):
+ geom = None
+ else:
+ # Otherwise, a GEOSGeometry object is built using the field's contents,
+ # and the model's corresponding attribute is set.
+ geom = self._klass(geom_value)
+ setattr(obj, self._field.attname, geom)
+ return geom
+
+ def __set__(self, obj, value):
+ """
+ This accessor sets the proxied geometry with the geometry class
+ specified during initialization. Values of None, HEXEWKB, or WKT may
+ be used to set the geometry as well.
+ """
+ # The OGC Geometry type of the field.
+ gtype = self._field._geom
+
+ # The geometry type must match that of the field -- unless the
+ # general GeometryField is used.
+ if isinstance(value, self._klass) and (str(value.geom_type).upper() == gtype or gtype == 'GEOMETRY'):
+ # Assigning the SRID to the geometry.
+ if value.srid is None: value.srid = self._field._srid
+ elif isinstance(value, (NoneType, StringType, UnicodeType)):
+ # Set with None, WKT, or HEX
+ pass
+ else:
+ raise TypeError('cannot set %s GeometryProxy with value of type: %s' % (obj.__class__.__name__, type(value)))
+
+ # Setting the objects dictionary with the value, and returning.
+ obj.__dict__[self._field.attname] = value
+ return value
diff --git a/django/contrib/gis/db/models/fields/__init__.py b/django/contrib/gis/db/models/fields/__init__.py
index 8d329fd97c..850cac2007 100644
--- a/django/contrib/gis/db/models/fields/__init__.py
+++ b/django/contrib/gis/db/models/fields/__init__.py
@@ -1,9 +1,9 @@
-from django.contrib.gis.db.backend import GeoBackendField # depends on the spatial database backend.
-from django.contrib.gis.db.models.proxy import GeometryProxy
+from django.conf import settings
+from django.contrib.gis.db.backend import GeoBackendField, GeometryProxy # these depend on the spatial database backend.
from django.contrib.gis.oldforms import WKTField
-from django.utils.functional import curry
+from django.contrib.gis.geos import GEOSGeometry
-#TODO: Flesh out widgets.
+#TODO: Flesh out widgets; consider adding support for OGR Geometry proxies.
class GeometryField(GeoBackendField):
"The base GIS field -- maps to the OpenGIS Specification Geometry type."
@@ -31,26 +31,9 @@ class GeometryField(GeoBackendField):
def contribute_to_class(self, cls, name):
super(GeometryField, self).contribute_to_class(cls, name)
- # setup for lazy-instantiated GEOSGeometry objects
- setattr(cls, self.attname, GeometryProxy(self))
-
- # Adding needed accessor functions
- setattr(cls, 'get_%s_geos' % self.name, curry(cls._get_GEOM_geos, field=self))
- setattr(cls, 'get_%s_ogr' % self.name, curry(cls._get_GEOM_ogr, field=self, srid=self._srid))
- setattr(cls, 'get_%s_srid' % self.name, curry(cls._get_GEOM_srid, srid=self._srid))
- setattr(cls, 'get_%s_srs' % self.name, curry(cls._get_GEOM_srs, srid=self._srid))
- setattr(cls, 'get_%s_wkt' % self.name, curry(cls._get_GEOM_wkt, field=self))
- setattr(cls, 'get_%s_centroid' % self.name, curry(cls._get_GEOM_centroid, field=self))
- setattr(cls, 'get_%s_area' % self.name, curry(cls._get_GEOM_area, field=self))
+ # Setup for lazy-instantiated GEOSGeometry object.
+ setattr(cls, self.attname, GeometryProxy(GEOSGeometry, self))
- def get_internal_type(self):
- return "NoField"
-
- def db_type(self):
- # Geometry columns are added by stored procedures, and thus should
- # be None.
- return None
-
def get_manipulator_field_objs(self):
"Using the WKTField (defined above) to be our manipulator."
return [WKTField]
diff --git a/django/contrib/gis/db/models/mixin.py b/django/contrib/gis/db/models/mixin.py
index 90640d1787..475a053b8f 100644
--- a/django/contrib/gis/db/models/mixin.py
+++ b/django/contrib/gis/db/models/mixin.py
@@ -1,54 +1,11 @@
-from warnings import warn
-
-# GEOS is a requirement, GDAL is not
-from django.contrib.gis.geos import GEOSGeometry
-from django.contrib.gis.gdal import HAS_GDAL
-if HAS_GDAL:
- from django.contrib.gis.gdal import OGRGeometry, SpatialReference
-
# Until model subclassing is a possibility, a mixin class is used to add
# the necessary functions that may be contributed for geographic objects.
class GeoMixin:
- "The Geographic Mixin class provides routines for geographic objects."
-
- # A subclass of Model is specifically needed so that these geographic
- # routines are present for instantiations of the models.
- def _get_GEOM_geos(self, field):
- "Returns a GEOS Python object for the geometry."
- warn("use model.%s" % field.attname, DeprecationWarning)
- return getattr(self, field.attname)
-
- def _get_GEOM_ogr(self, field, srid):
- "Returns an OGR Python object for the geometry."
- if HAS_GDAL:
- return OGRGeometry(getattr(self, field.attname).wkt,
- SpatialReference('EPSG:%d' % srid))
- else:
- raise Exception, "GDAL is not installed!"
-
- def _get_GEOM_srid(self, srid):
- "Returns the spatial reference identifier (SRID) of the geometry."
- warn("use model.geometry_field.srid", DeprecationWarning)
- return srid
-
- def _get_GEOM_srs(self, srid):
- "Returns ane OGR Spatial Reference object of the geometry."
- if HAS_GDAL:
- return SpatialReference('EPSG:%d' % srid)
- else:
- raise Exception, "GDAL is not installed!"
-
- def _get_GEOM_wkt(self, field):
- "Returns the WKT of the geometry."
- warn("use model.%s.centroid.wkt" % field.attname, DeprecationWarning)
- return getattr(self, field.attname).wkt
-
- def _get_GEOM_centroid(self, field):
- "Returns the centroid of the geometry, in WKT."
- warn("use model.%s.centroid.wkt" % field.attname, DeprecationWarning)
- return getattr(self, field.attname).centroid.wkt
-
- def _get_GEOM_area(self, field):
- "Returns the area of the geometry, in projected units."
- warn("use model.%s.area" % field.attname, DeprecationWarning)
- return getattr(self, field.attname).area
+ """
+ The Geographic Mixin class provides routines for geographic objects,
+ however, it is no longer necessary, since all of its previous functions
+ may now be accessed via the GeometryProxy. This mixin is only provided
+ for backwards-compatibility purposes, and will be eventually removed
+ (unless the need arises again).
+ """
+ pass
diff --git a/django/contrib/gis/db/models/proxy.py b/django/contrib/gis/db/models/proxy.py
deleted file mode 100644
index 65c613da26..0000000000
--- a/django/contrib/gis/db/models/proxy.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
- The GeometryProxy object, allows for lazy-geometries. The proxy uses
- Python descriptors for instantiating and setting GEOS Geometry objects
- corresponding to geographic model fields.
-
- Thanks to Robert Coup for providing this functionality (see #4322).
-"""
-
-from types import NoneType, StringType, UnicodeType
-from django.contrib.gis.geos import GEOSGeometry, GEOSException
-
-# TODO: docstrings
-class GeometryProxy(object):
- def __init__(self, field):
- "Proxy initializes on the given GeometryField."
- self._field = field
-
- def __get__(self, obj, type=None):
- # Getting the value of the field.
- geom_value = obj.__dict__[self._field.attname]
-
- if isinstance(geom_value, GEOSGeometry):
- # If the value of the field is None, or is already a GEOS Geometry
- # no more work is needed.
- geom = geom_value
- elif (geom_value is None) or (geom_value==''):
- geom = None
- else:
- # Otherwise, a GEOSGeometry object is built using the field's contents,
- # and the model's corresponding attribute is set.
- geom = GEOSGeometry(geom_value)
- setattr(obj, self._field.attname, geom)
- return geom
-
- def __set__(self, obj, value):
- if isinstance(value, GEOSGeometry) and (value.geom_type.upper() == self._field._geom):
- # Getting set with GEOS Geometry; geom_type must match that of the field.
-
- # If value's SRID is not set, setting it to the field's SRID.
- if value.srid is None: value.srid = self._field._srid
- elif isinstance(value, (NoneType, StringType, UnicodeType)):
- # Getting set with None, WKT, or HEX
- pass
- else:
- raise TypeError, 'cannot set %s GeometryProxy with value of type: %s' % (self._field._geom, type(value))
- obj.__dict__[self._field.attname] = value
- return value
diff --git a/django/contrib/gis/models.py b/django/contrib/gis/models.py
index 91ca8d7818..5a4e480837 100644
--- a/django/contrib/gis/models.py
+++ b/django/contrib/gis/models.py
@@ -1,8 +1,9 @@
"""
- Models for the PostGIS/OGC database tables.
+ Imports the SpatialRefSys and GeometryColumns models dependent on the
+ spatial database backend.
"""
import re
-from django.db import models
+from django.conf import settings
# Checking for the presence of GDAL (needed for the SpatialReference object)
from django.contrib.gis.gdal import HAS_GDAL
@@ -15,37 +16,11 @@ if HAS_GDAL:
# parameter.
spheroid_regex = re.compile(r'.+SPHEROID\[\"(?P<name>.+)\",(?P<major>\d+(\.\d+)?),(?P<flattening>\d{3}\.\d+),')
-# This is the global 'geometry_columns' from PostGIS.
-# See PostGIS Documentation at Ch. 4.2.2
-class GeometryColumns(models.Model):
- f_table_catalog = models.CharField(maxlength=256)
- f_table_schema = models.CharField(maxlength=256)
- f_table_name = models.CharField(maxlength=256, primary_key=True)
- f_geometry_column = models.CharField(maxlength=256)
- coord_dimension = models.IntegerField()
- srid = models.IntegerField()
- type = models.CharField(maxlength=30)
-
- class Meta:
- db_table = 'geometry_columns'
-
- def __str__(self):
- return "%s.%s - %dD %s field (SRID: %d)" % \
- (self.f_table_name, self.f_geometry_column,
- self.coord_dimension, self.type, self.srid)
-
-# This is the global 'spatial_ref_sys' table from PostGIS.
-# See PostGIS Documentation at Ch. 4.2.1
-class SpatialRefSys(models.Model):
- srid = models.IntegerField(primary_key=True)
- auth_name = models.CharField(maxlength=256)
- auth_srid = models.IntegerField()
- srtext = models.CharField(maxlength=2048)
- proj4text = models.CharField(maxlength=2048)
-
- class Meta:
- db_table = 'spatial_ref_sys'
-
+class SpatialRefSysMixin(object):
+ """
+ The SpatialRefSysMixin is a class used by the database-dependent
+ SpatialRefSys objects to reduce redundnant code.
+ """
@property
def srs(self):
"""
@@ -58,24 +33,17 @@ class SpatialRefSys(models.Model):
else:
# Attempting to cache a SpatialReference object.
- # Trying to get from WKT first
+ # Trying to get from WKT first.
try:
- self._srs = SpatialReference(self.srtext, 'wkt')
- return self._srs.clone()
- except Exception, msg1:
+ self._srs = SpatialReference(self.wkt)
+ return self.srs
+ except Exception, msg:
pass
-
- # Trying the proj4 text next
- try:
- self._srs = SpatialReference(self.proj4text, 'proj4')
- return self._srs.clone()
- except Exception, msg2:
- pass
-
- raise Exception, 'Could not get an OSR Spatial Reference:\n\tWKT error: %s\n\tPROJ.4 error: %s' % (msg1, msg2)
+
+ raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
else:
- raise Exception, 'GDAL is not installed!'
-
+ raise Exception('GDAL is not installed.')
+
@property
def ellipsoid(self):
"""
@@ -85,7 +53,7 @@ class SpatialRefSys(models.Model):
if HAS_GDAL:
return self.srs.ellipsoid
else:
- m = spheroid_regex.match(self.srtext)
+ m = spheroid_regex.match(self.wkt)
if m: return (float(m.group('major')), float(m.group('flattening')))
else: return None
@@ -139,10 +107,18 @@ class SpatialRefSys(models.Model):
"Returns the name of the angular units."
return self.srs.angular_name
- def __str__(self):
+ def __unicode__(self):
"""
Returns the string representation. If GDAL is installed,
it will be 'pretty' OGC WKT.
"""
- if HAS_GDAL: return str(self.srs)
- else: return "%d:%s " % (self.srid, self.auth_name)
+ try:
+ return unicode(self.srs)
+ except:
+ return unicode(self.srtext)
+
+# The SpatialRefSys and GeometryColumns models
+if settings.DATABASE_ENGINE == 'postgresql_psycopg2':
+ from django.contrib.gis.db.backend.postgis.models import GeometryColumns, SpatialRefSys
+else:
+ raise NotImplementedError('No SpatialRefSys or GeometryColumns models for backend: %s' % settings.DATABASE_ENGINE)
diff --git a/django/contrib/gis/tests/geoapp/models.py b/django/contrib/gis/tests/geoapp/models.py
index fa979beead..4315b4247c 100644
--- a/django/contrib/gis/tests/geoapp/models.py
+++ b/django/contrib/gis/tests/geoapp/models.py
@@ -1,16 +1,21 @@
from django.contrib.gis.db import models
-class Country(models.Model, models.GeoMixin):
- name = models.CharField(maxlength=30)
+class Country(models.Model):
+ name = models.CharField(max_length=30)
mpoly = models.MultiPolygonField() # SRID, by default, is 4326
objects = models.GeoManager()
-class City(models.Model, models.GeoMixin):
- name = models.CharField(maxlength=30)
+class City(models.Model):
+ name = models.CharField(max_length=30)
point = models.PointField()
objects = models.GeoManager()
-class State(models.Model, models.GeoMixin):
- name = models.CharField(maxlength=30)
+class State(models.Model):
+ name = models.CharField(max_length=30)
poly = models.PolygonField(null=True) # Allowing NULL geometries here.
objects = models.GeoManager()
+
+class Feature(models.Model):
+ name = models.CharField(max_length=20)
+ geom = models.GeometryField()
+ objects = models.GeoManager()
diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py
index e8127c0827..004e75898e 100644
--- a/django/contrib/gis/tests/geoapp/tests.py
+++ b/django/contrib/gis/tests/geoapp/tests.py
@@ -1,6 +1,7 @@
import unittest
-from models import Country, City, State
-from django.contrib.gis.geos import fromstr, Point, LineString, LinearRing, Polygon
+from models import Country, City, State, Feature
+from django.contrib.gis.geos import *
+from django.contrib.gis import gdal
class GeoModelTest(unittest.TestCase):
@@ -59,8 +60,17 @@ class GeoModelTest(unittest.TestCase):
nullstate = State(name='NullState', poly=ply)
self.assertEqual(4326, nullstate.poly.srid) # SRID auto-set from None
nullstate.save()
- self.assertEqual(ply, State.objects.get(name='NullState').poly)
+
+ ns = State.objects.get(name='NullState')
+ self.assertEqual(ply, ns.poly)
+ # Testing the `ogr` and `srs` lazy-geometry properties.
+ if gdal.HAS_GDAL:
+ self.assertEqual(True, isinstance(ns.poly.ogr, gdal.OGRGeometry))
+ self.assertEqual(ns.poly.wkb, ns.poly.ogr.wkb)
+ self.assertEqual(True, isinstance(ns.poly.srs, gdal.SpatialReference))
+ self.assertEqual('WGS 84', ns.poly.srs.name)
+
# Changing the interior ring on the poly attribute.
new_inner = LinearRing((30, 30), (30, 70), (70, 70), (70, 30), (30, 30))
nullstate.poly[1] = new_inner.clone()
@@ -277,6 +287,32 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(True, union.equals_exact(u, 10)) # Going up to 10 digits of precision.
qs = City.objects.filter(name='NotACity')
self.assertEqual(None, qs.union('point'))
+
+ def test18_geometryfield(self):
+ "Testing GeometryField."
+ f1 = Feature(name='Point', geom=Point(1, 1))
+ f2 = Feature(name='LineString', geom=LineString((0, 0), (1, 1), (5, 5)))
+ f3 = Feature(name='Polygon', geom=Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))))
+ f4 = Feature(name='GeometryCollection',
+ geom=GeometryCollection(Point(2, 2), LineString((0, 0), (2, 2)),
+ Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)))))
+ f1.save()
+ f2.save()
+ f3.save()
+ f4.save()
+
+ f_1 = Feature.objects.get(name='Point')
+ self.assertEqual(True, isinstance(f_1.geom, Point))
+ self.assertEqual((1.0, 1.0), f_1.geom.tuple)
+ f_2 = Feature.objects.get(name='LineString')
+ self.assertEqual(True, isinstance(f_2.geom, LineString))
+ self.assertEqual(((0.0, 0.0), (1.0, 1.0), (5.0, 5.0)), f_2.geom.tuple)
+
+ f_3 = Feature.objects.get(name='Polygon')
+ self.assertEqual(True, isinstance(f_3.geom, Polygon))
+ f_4 = Feature.objects.get(name='GeometryCollection')
+ self.assertEqual(True, isinstance(f_4.geom, GeometryCollection))
+ self.assertEqual(f_3.geom, f_4.geom[2])
def suite():
s = unittest.TestSuite()
diff --git a/django/core/management/sql.py b/django/core/management/sql.py
index 1757d4d7a0..d69d83e44e 100644
--- a/django/core/management/sql.py
+++ b/django/core/management/sql.py
@@ -404,7 +404,7 @@ def custom_sql_for_model(model, style):
# Post-creation SQL should come before any initial SQL data is loaded.
for f in opts.fields:
if hasattr(f, '_post_create_sql'):
- output.append(f._post_create_sql(style, model._meta.db_table))
+ output.extend(f._post_create_sql(style, model._meta.db_table))
# Some backends can't execute more than one SQL statement at a time,
# so split into separate statements.