Fixed #9364 -- now uses the appropriate database table for inherited `GeometryField`s; now uses the `SpatialBackend` booleans in tests.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9336 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2008-11-05 17:51:11 +00:00
parent 1515b13d1a
commit b401d44a9f
3 changed files with 43 additions and 20 deletions

View File

@ -605,13 +605,19 @@ class GeoQuerySet(QuerySet):
# set on the `GeoQuery` object of this queryset. # set on the `GeoQuery` object of this queryset.
if aggregate: self.query.aggregate = True if aggregate: self.query.aggregate = True
opts = self.model._meta
if not geo_field in opts.fields:
# Is this operation going to be on a related geographic field? # Is this operation going to be on a related geographic field?
if not geo_field in self.model._meta.fields:
# If so, it'll have to be added to the select related information # If so, it'll have to be added to the select related information
# (e.g., if 'location__point' was given as the field name). # (e.g., if 'location__point' was given as the field name).
self.query.add_select_related([field_name]) self.query.add_select_related([field_name])
self.query.pre_sql_setup() self.query.pre_sql_setup()
rel_table, rel_col = self.query.related_select_cols[self.query.related_select_fields.index(geo_field)] rel_table, rel_col = self.query.related_select_cols[self.query.related_select_fields.index(geo_field)]
return self.query._field_column(geo_field, rel_table) return self.query._field_column(geo_field, rel_table)
elif not geo_field in opts.local_fields:
# This geographic field is inherited from another model, so we have to
# use the db table for the _parent_ model instead.
tmp_fld, parent_model, direct, m2m = opts.get_field_by_name(geo_field.name)
return self.query._field_column(geo_field, parent_model._meta.db_table)
else: else:
return self.query._field_column(geo_field) return self.query._field_column(geo_field)

View File

@ -16,6 +16,11 @@ class City(models.Model):
objects = models.GeoManager() objects = models.GeoManager()
def __unicode__(self): return self.name def __unicode__(self): return self.name
# This is an inherited model from City
class PennsylvaniaCity(City):
county = models.CharField(max_length=30)
objects = models.GeoManager() # TODO: This should be implicitly inherited.
class State(models.Model): class State(models.Model):
name = models.CharField(max_length=30) name = models.CharField(max_length=30)
poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here. poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here.

View File

@ -1,10 +1,10 @@
import os, unittest import os, unittest
from models import Country, City, State, Feature, MinusOneSRID from models import Country, City, PennsylvaniaCity, State, Feature, MinusOneSRID
from django.contrib.gis import gdal from django.contrib.gis import gdal
from django.contrib.gis.db.backend import SpatialBackend from django.contrib.gis.db.backend import SpatialBackend
from django.contrib.gis.geos import * from django.contrib.gis.geos import *
from django.contrib.gis.measure import Distance from django.contrib.gis.measure import Distance
from django.contrib.gis.tests.utils import no_oracle, no_postgis, oracle, postgis from django.contrib.gis.tests.utils import no_oracle, no_postgis
# TODO: Some tests depend on the success/failure of previous tests, these should # TODO: Some tests depend on the success/failure of previous tests, these should
# be decoupled. This flag is an artifact of this problem, and makes debugging easier; # be decoupled. This flag is an artifact of this problem, and makes debugging easier;
@ -17,7 +17,7 @@ class GeoModelTest(unittest.TestCase):
def test01_initial_sql(self): def test01_initial_sql(self):
"Testing geographic initial SQL." "Testing geographic initial SQL."
if DISABLE: return if DISABLE: return
if oracle: if SpatialBackend.oracle:
# Oracle doesn't allow strings longer than 4000 characters # Oracle doesn't allow strings longer than 4000 characters
# in SQL files, and I'm stumped on how to use Oracle BFILE's # in SQL files, and I'm stumped on how to use Oracle BFILE's
# in PLSQL, so we set up the larger geometries manually, rather # in PLSQL, so we set up the larger geometries manually, rather
@ -38,7 +38,7 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(8, City.objects.count()) self.assertEqual(8, City.objects.count())
# Oracle cannot handle NULL geometry values w/certain queries. # Oracle cannot handle NULL geometry values w/certain queries.
if oracle: n_state = 2 if SpatialBackend.oracle: n_state = 2
else: n_state = 3 else: n_state = 3
self.assertEqual(n_state, State.objects.count()) self.assertEqual(n_state, State.objects.count())
@ -147,7 +147,7 @@ class GeoModelTest(unittest.TestCase):
ptown1 = City.objects.gml(field_name='point', precision=9).get(name='Pueblo') ptown1 = City.objects.gml(field_name='point', precision=9).get(name='Pueblo')
ptown2 = City.objects.gml(precision=9).get(name='Pueblo') ptown2 = City.objects.gml(precision=9).get(name='Pueblo')
if oracle: if SpatialBackend.oracle:
# No precision parameter for Oracle :-/ # No precision parameter for Oracle :-/
import re import re
gml_regex = re.compile(r'<gml:Point srsName="SDO:4326" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="\." cs="," ts=" ">-104.60925199\d+,38.25500\d+ </gml:coordinates></gml:Point>') gml_regex = re.compile(r'<gml:Point srsName="SDO:4326" xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="\." cs="," ts=" ">-104.60925199\d+,38.25500\d+ </gml:coordinates></gml:Point>')
@ -167,7 +167,7 @@ class GeoModelTest(unittest.TestCase):
# Asserting the result of the transform operation with the values in # Asserting the result of the transform operation with the values in
# the pre-transformed points. Oracle does not have the 3084 SRID. # the pre-transformed points. Oracle does not have the 3084 SRID.
if not oracle: if not SpatialBackend.oracle:
h = City.objects.transform(htown.srid).get(name='Houston') h = City.objects.transform(htown.srid).get(name='Houston')
self.assertEqual(3084, h.point.srid) self.assertEqual(3084, h.point.srid)
self.assertAlmostEqual(htown.x, h.point.x, prec) self.assertAlmostEqual(htown.x, h.point.x, prec)
@ -214,7 +214,7 @@ class GeoModelTest(unittest.TestCase):
qs1 = City.objects.filter(point__disjoint=ptown.point) qs1 = City.objects.filter(point__disjoint=ptown.point)
self.assertEqual(7, qs1.count()) self.assertEqual(7, qs1.count())
if not postgis: if not SpatialBackend.postgis:
# TODO: Do NULL columns bork queries on PostGIS? The following # TODO: Do NULL columns bork queries on PostGIS? The following
# error is encountered: # error is encountered:
# psycopg2.ProgrammingError: invalid memory alloc request size 4294957297 # psycopg2.ProgrammingError: invalid memory alloc request size 4294957297
@ -231,7 +231,7 @@ class GeoModelTest(unittest.TestCase):
# Seeing what cities are in Texas, should get Houston and Dallas, # Seeing what cities are in Texas, should get Houston and Dallas,
# and Oklahoma City because 'contained' only checks on the # and Oklahoma City because 'contained' only checks on the
# _bounding box_ of the Geometries. # _bounding box_ of the Geometries.
if not oracle: if not SpatialBackend.oracle:
qs = City.objects.filter(point__contained=texas.mpoly) qs = City.objects.filter(point__contained=texas.mpoly)
self.assertEqual(3, qs.count()) self.assertEqual(3, qs.count())
cities = ['Houston', 'Dallas', 'Oklahoma City'] cities = ['Houston', 'Dallas', 'Oklahoma City']
@ -259,7 +259,7 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(0, len(Country.objects.filter(mpoly__contains=okcity.point.wkt))) # Qeury w/WKT self.assertEqual(0, len(Country.objects.filter(mpoly__contains=okcity.point.wkt))) # Qeury w/WKT
# OK City is contained w/in bounding box of Texas. # OK City is contained w/in bounding box of Texas.
if not oracle: if not SpatialBackend.oracle:
qs = Country.objects.filter(mpoly__bbcontains=okcity.point) qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
self.assertEqual(1, len(qs)) self.assertEqual(1, len(qs))
self.assertEqual('Texas', qs[0].name) self.assertEqual('Texas', qs[0].name)
@ -272,7 +272,7 @@ class GeoModelTest(unittest.TestCase):
wgs_pnt = fromstr(sa_4326, srid=4326) # Our reference point in WGS84 wgs_pnt = fromstr(sa_4326, srid=4326) # Our reference point in WGS84
# Oracle doesn't have SRID 3084, using 41157. # Oracle doesn't have SRID 3084, using 41157.
if oracle: if SpatialBackend.oracle:
# San Antonio in 'Texas 4205, Southern Zone (1983, meters)' (SRID 41157) # San Antonio in 'Texas 4205, Southern Zone (1983, meters)' (SRID 41157)
# Used the following Oracle SQL to get this value: # Used the following Oracle SQL to get this value:
# SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM(SDO_GEOMETRY('POINT (-98.493183 29.424170)', 4326), 41157)) FROM DUAL; # SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM(SDO_GEOMETRY('POINT (-98.493183 29.424170)', 4326), 41157)) FROM DUAL;
@ -287,7 +287,7 @@ class GeoModelTest(unittest.TestCase):
# `SDO_OVERLAPBDYINTERSECT` operates differently from # `SDO_OVERLAPBDYINTERSECT` operates differently from
# `ST_Intersects`, so contains is used instead. # `ST_Intersects`, so contains is used instead.
nad_pnt = fromstr(nad_wkt, srid=nad_srid) nad_pnt = fromstr(nad_wkt, srid=nad_srid)
if oracle: if SpatialBackend.oracle:
tx = Country.objects.get(mpoly__contains=nad_pnt) tx = Country.objects.get(mpoly__contains=nad_pnt)
else: else:
tx = Country.objects.get(mpoly__intersects=nad_pnt) tx = Country.objects.get(mpoly__intersects=nad_pnt)
@ -329,7 +329,7 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(True, 'Kansas' in state_names) self.assertEqual(True, 'Kansas' in state_names)
# Saving another commonwealth w/a NULL geometry. # Saving another commonwealth w/a NULL geometry.
if not oracle: if not SpatialBackend.oracle:
# TODO: Fix saving w/NULL geometry on Oracle. # TODO: Fix saving w/NULL geometry on Oracle.
State(name='Northern Mariana Islands', poly=None).save() State(name='Northern Mariana Islands', poly=None).save()
@ -398,11 +398,11 @@ class GeoModelTest(unittest.TestCase):
self.assertRaises(e, qs.count) self.assertRaises(e, qs.count)
# Relate works differently for the different backends. # Relate works differently for the different backends.
if postgis: if SpatialBackend.postgis:
contains_mask = 'T*T***FF*' contains_mask = 'T*T***FF*'
within_mask = 'T*F**F***' within_mask = 'T*F**F***'
intersects_mask = 'T********' intersects_mask = 'T********'
elif oracle: elif SpatialBackend.oracle:
contains_mask = 'contains' contains_mask = 'contains'
within_mask = 'inside' within_mask = 'inside'
# TODO: This is not quite the same as the PostGIS mask above # TODO: This is not quite the same as the PostGIS mask above
@ -417,7 +417,7 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, within_mask)).name) self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, within_mask)).name)
# Testing intersection relation mask. # Testing intersection relation mask.
if not oracle: if not SpatialBackend.oracle:
self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name) self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name)
self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name) self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name)
self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name) self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name)
@ -479,7 +479,7 @@ class GeoModelTest(unittest.TestCase):
"Testing the `centroid` GeoQuerySet method." "Testing the `centroid` GeoQuerySet method."
if DISABLE: return if DISABLE: return
qs = State.objects.exclude(poly__isnull=True).centroid() qs = State.objects.exclude(poly__isnull=True).centroid()
if oracle: tol = 0.1 if SpatialBackend.oracle: tol = 0.1
else: tol = 0.000000001 else: tol = 0.000000001
for s in qs: for s in qs:
self.assertEqual(True, s.poly.centroid.equals_exact(s.centroid, tol)) self.assertEqual(True, s.poly.centroid.equals_exact(s.centroid, tol))
@ -536,14 +536,14 @@ class GeoModelTest(unittest.TestCase):
for c in City.objects.filter(point__isnull=False).num_geom(): for c in City.objects.filter(point__isnull=False).num_geom():
# Oracle will return 1 for the number of geometries on non-collections, # Oracle will return 1 for the number of geometries on non-collections,
# whereas PostGIS will return None. # whereas PostGIS will return None.
if postgis: self.assertEqual(None, c.num_geom) if SpatialBackend.postgis: self.assertEqual(None, c.num_geom)
else: self.assertEqual(1, c.num_geom) else: self.assertEqual(1, c.num_geom)
def test24_numpoints(self): def test24_numpoints(self):
"Testing the `num_points` GeoQuerySet method." "Testing the `num_points` GeoQuerySet method."
if DISABLE: return if DISABLE: return
for c in Country.objects.num_points(): self.assertEqual(c.mpoly.num_points, c.num_points) for c in Country.objects.num_points(): self.assertEqual(c.mpoly.num_points, c.num_points)
if postgis: if SpatialBackend.postgis:
# Oracle cannot count vertices in Point geometries. # Oracle cannot count vertices in Point geometries.
for c in City.objects.num_points(): self.assertEqual(1, c.num_points) for c in City.objects.num_points(): self.assertEqual(1, c.num_points)
@ -558,6 +558,18 @@ class GeoModelTest(unittest.TestCase):
self.assertEqual(c.mpoly.sym_difference(geom), c.sym_difference) self.assertEqual(c.mpoly.sym_difference(geom), c.sym_difference)
self.assertEqual(c.mpoly.union(geom), c.union) self.assertEqual(c.mpoly.union(geom), c.union)
def test26_inherited_geofields(self):
"Test GeoQuerySet methods on inherited Geometry fields."
# Creating a Pennsylvanian city.
mansfield = PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)')
# All transformation SQL will need to be performed on the
# _parent_ table.
qs = PennsylvaniaCity.objects.transform(32128)
self.assertEqual(1, qs.count())
for pc in qs: self.assertEqual(32128, pc.point.srid)
from test_feeds import GeoFeedTest from test_feeds import GeoFeedTest
from test_sitemaps import GeoSitemapTest from test_sitemaps import GeoSitemapTest
def suite(): def suite():