diff --git a/django/contrib/gis/db/models/query.py b/django/contrib/gis/db/models/query.py index 8efc720333..b7b7dcda93 100644 --- a/django/contrib/gis/db/models/query.py +++ b/django/contrib/gis/db/models/query.py @@ -605,13 +605,19 @@ class GeoQuerySet(QuerySet): # set on the `GeoQuery` object of this queryset. if aggregate: self.query.aggregate = True - # Is this operation going to be on a related geographic field? - if not geo_field in self.model._meta.fields: + opts = self.model._meta + if not geo_field in opts.fields: + # Is this operation going to be on a related geographic field? # If so, it'll have to be added to the select related information # (e.g., if 'location__point' was given as the field name). self.query.add_select_related([field_name]) self.query.pre_sql_setup() 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) + 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: return self.query._field_column(geo_field) diff --git a/django/contrib/gis/tests/geoapp/models.py b/django/contrib/gis/tests/geoapp/models.py index fcb06e16a6..9843673167 100644 --- a/django/contrib/gis/tests/geoapp/models.py +++ b/django/contrib/gis/tests/geoapp/models.py @@ -16,6 +16,11 @@ class City(models.Model): objects = models.GeoManager() 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): name = models.CharField(max_length=30) poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here. diff --git a/django/contrib/gis/tests/geoapp/tests.py b/django/contrib/gis/tests/geoapp/tests.py index ff6eac9cc7..fa93cbce1c 100644 --- a/django/contrib/gis/tests/geoapp/tests.py +++ b/django/contrib/gis/tests/geoapp/tests.py @@ -1,10 +1,10 @@ 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.db.backend import SpatialBackend from django.contrib.gis.geos import * 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 # 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): "Testing geographic initial SQL." if DISABLE: return - if oracle: + if SpatialBackend.oracle: # Oracle doesn't allow strings longer than 4000 characters # 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 @@ -38,7 +38,7 @@ class GeoModelTest(unittest.TestCase): self.assertEqual(8, City.objects.count()) # Oracle cannot handle NULL geometry values w/certain queries. - if oracle: n_state = 2 + if SpatialBackend.oracle: n_state = 2 else: n_state = 3 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') ptown2 = City.objects.gml(precision=9).get(name='Pueblo') - if oracle: + if SpatialBackend.oracle: # No precision parameter for Oracle :-/ import re gml_regex = re.compile(r'-104.60925199\d+,38.25500\d+ ') @@ -167,7 +167,7 @@ class GeoModelTest(unittest.TestCase): # Asserting the result of the transform operation with the values in # 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') self.assertEqual(3084, h.point.srid) self.assertAlmostEqual(htown.x, h.point.x, prec) @@ -214,7 +214,7 @@ class GeoModelTest(unittest.TestCase): qs1 = City.objects.filter(point__disjoint=ptown.point) self.assertEqual(7, qs1.count()) - if not postgis: + if not SpatialBackend.postgis: # TODO: Do NULL columns bork queries on PostGIS? The following # error is encountered: # 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, # and Oklahoma City because 'contained' only checks on the # _bounding box_ of the Geometries. - if not oracle: + if not SpatialBackend.oracle: qs = City.objects.filter(point__contained=texas.mpoly) self.assertEqual(3, qs.count()) 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 # 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) self.assertEqual(1, len(qs)) 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 # Oracle doesn't have SRID 3084, using 41157. - if oracle: + if SpatialBackend.oracle: # San Antonio in 'Texas 4205, Southern Zone (1983, meters)' (SRID 41157) # 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; @@ -287,7 +287,7 @@ class GeoModelTest(unittest.TestCase): # `SDO_OVERLAPBDYINTERSECT` operates differently from # `ST_Intersects`, so contains is used instead. nad_pnt = fromstr(nad_wkt, srid=nad_srid) - if oracle: + if SpatialBackend.oracle: tx = Country.objects.get(mpoly__contains=nad_pnt) else: tx = Country.objects.get(mpoly__intersects=nad_pnt) @@ -329,7 +329,7 @@ class GeoModelTest(unittest.TestCase): self.assertEqual(True, 'Kansas' in state_names) # Saving another commonwealth w/a NULL geometry. - if not oracle: + if not SpatialBackend.oracle: # TODO: Fix saving w/NULL geometry on Oracle. State(name='Northern Mariana Islands', poly=None).save() @@ -398,11 +398,11 @@ class GeoModelTest(unittest.TestCase): self.assertRaises(e, qs.count) # Relate works differently for the different backends. - if postgis: + if SpatialBackend.postgis: contains_mask = 'T*T***FF*' within_mask = 'T*F**F***' intersects_mask = 'T********' - elif oracle: + elif SpatialBackend.oracle: contains_mask = 'contains' within_mask = 'inside' # 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) # 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=(pnt2, 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." if DISABLE: return qs = State.objects.exclude(poly__isnull=True).centroid() - if oracle: tol = 0.1 + if SpatialBackend.oracle: tol = 0.1 else: tol = 0.000000001 for s in qs: 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(): # Oracle will return 1 for the number of geometries on non-collections, # 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) def test24_numpoints(self): "Testing the `num_points` GeoQuerySet method." if DISABLE: return 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. 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.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_sitemaps import GeoSitemapTest def suite():