Ticket #9364: handle_inherited_geofields.diff

File handle_inherited_geofields.diff, 10.1 KB (added by jbronn, 7 years ago)

Patch that allows GeoQuerySet methods to operate on inherited geometry fields; includes tests.

  • django/contrib/gis/db/models/query.py

     
    605605        # set on the `GeoQuery` object of this queryset.
    606606        if aggregate: self.query.aggregate = True
    607607
    608         # Is this operation going to be on a related geographic field?
    609         if not geo_field in self.model._meta.fields:
     608        opts = self.model._meta
     609        if not geo_field in opts.fields:
     610            # Is this operation going to be on a related geographic field?
    610611            # If so, it'll have to be added to the select related information
    611612            # (e.g., if 'location__point' was given as the field name).
    612613            self.query.add_select_related([field_name])
    613614            self.query.pre_sql_setup()
    614615            rel_table, rel_col = self.query.related_select_cols[self.query.related_select_fields.index(geo_field)]
    615616            return self.query._field_column(geo_field, rel_table)
     617        elif not geo_field in opts.local_fields:
     618            # This geographic field is inherited from another model, so we have to
     619            # use the db table for the _parent_ model instead.
     620            tmp_fld, parent_model, direct, m2m = opts.get_field_by_name(geo_field.name)
     621            return self.query._field_column(geo_field, parent_model._meta.db_table)
    616622        else:
    617623            return self.query._field_column(geo_field)
  • django/contrib/gis/tests/geoapp/tests.py

     
    11import os, unittest
    2 from models import Country, City, State, Feature, MinusOneSRID
     2from models import Country, City, PennsylvaniaCity, State, Feature, MinusOneSRID
    33from django.contrib.gis import gdal
    44from django.contrib.gis.db.backend import SpatialBackend
    55from django.contrib.gis.geos import *
    66from django.contrib.gis.measure import Distance
    7 from django.contrib.gis.tests.utils import no_oracle, no_postgis, oracle, postgis
     7from django.contrib.gis.tests.utils import no_oracle, no_postgis
    88
    99# TODO: Some tests depend on the success/failure of previous tests, these should
    1010# be decoupled.  This flag is an artifact of this problem, and makes debugging easier;
     
    1717    def test01_initial_sql(self):
    1818        "Testing geographic initial SQL."
    1919        if DISABLE: return
    20         if oracle:
     20        if SpatialBackend.oracle:
    2121            # Oracle doesn't allow strings longer than 4000 characters
    2222            # in SQL files, and I'm stumped on how to use Oracle BFILE's
    2323            # in PLSQL, so we set up the larger geometries manually, rather
     
    3838        self.assertEqual(8, City.objects.count())
    3939
    4040        # Oracle cannot handle NULL geometry values w/certain queries.
    41         if oracle: n_state = 2
     41        if SpatialBackend.oracle: n_state = 2
    4242        else: n_state = 3
    4343        self.assertEqual(n_state, State.objects.count())
    4444
     
    147147        ptown1 = City.objects.gml(field_name='point', precision=9).get(name='Pueblo')
    148148        ptown2 = City.objects.gml(precision=9).get(name='Pueblo')
    149149
    150         if oracle:
     150        if SpatialBackend.oracle:
    151151            # No precision parameter for Oracle :-/
    152152            import re
    153153            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>')
     
    167167
    168168        # Asserting the result of the transform operation with the values in
    169169        #  the pre-transformed points.  Oracle does not have the 3084 SRID.
    170         if not oracle:
     170        if not SpatialBackend.oracle:
    171171            h = City.objects.transform(htown.srid).get(name='Houston')
    172172            self.assertEqual(3084, h.point.srid)
    173173            self.assertAlmostEqual(htown.x, h.point.x, prec)
     
    214214        qs1 = City.objects.filter(point__disjoint=ptown.point)
    215215        self.assertEqual(7, qs1.count())
    216216
    217         if not postgis:
     217        if not SpatialBackend.postgis:
    218218            # TODO: Do NULL columns bork queries on PostGIS?  The following
    219219            # error is encountered:
    220220            #  psycopg2.ProgrammingError: invalid memory alloc request size 4294957297
     
    231231        # Seeing what cities are in Texas, should get Houston and Dallas,
    232232        #  and Oklahoma City because 'contained' only checks on the
    233233        #  _bounding box_ of the Geometries.
    234         if not oracle:
     234        if not SpatialBackend.oracle:
    235235            qs = City.objects.filter(point__contained=texas.mpoly)
    236236            self.assertEqual(3, qs.count())
    237237            cities = ['Houston', 'Dallas', 'Oklahoma City']
     
    259259        self.assertEqual(0, len(Country.objects.filter(mpoly__contains=okcity.point.wkt))) # Qeury w/WKT
    260260
    261261        # OK City is contained w/in bounding box of Texas.
    262         if not oracle:
     262        if not SpatialBackend.oracle:
    263263            qs = Country.objects.filter(mpoly__bbcontains=okcity.point)
    264264            self.assertEqual(1, len(qs))
    265265            self.assertEqual('Texas', qs[0].name)
     
    272272        wgs_pnt = fromstr(sa_4326, srid=4326) # Our reference point in WGS84
    273273
    274274        # Oracle doesn't have SRID 3084, using 41157.
    275         if oracle:
     275        if SpatialBackend.oracle:
    276276            # San Antonio in 'Texas 4205, Southern Zone (1983, meters)' (SRID 41157)
    277277            # Used the following Oracle SQL to get this value:
    278278            #  SELECT SDO_UTIL.TO_WKTGEOMETRY(SDO_CS.TRANSFORM(SDO_GEOMETRY('POINT (-98.493183 29.424170)', 4326), 41157)) FROM DUAL;
     
    287287        # `SDO_OVERLAPBDYINTERSECT` operates differently from
    288288        # `ST_Intersects`, so contains is used instead.
    289289        nad_pnt = fromstr(nad_wkt, srid=nad_srid)
    290         if oracle:
     290        if SpatialBackend.oracle:
    291291            tx = Country.objects.get(mpoly__contains=nad_pnt)
    292292        else:
    293293            tx = Country.objects.get(mpoly__intersects=nad_pnt)
     
    329329        self.assertEqual(True, 'Kansas' in state_names)
    330330
    331331        # Saving another commonwealth w/a NULL geometry.
    332         if not oracle:
     332        if not SpatialBackend.oracle:
    333333            # TODO: Fix saving w/NULL geometry on Oracle.
    334334            State(name='Northern Mariana Islands', poly=None).save()
    335335
     
    398398            self.assertRaises(e, qs.count)
    399399
    400400        # Relate works differently for the different backends.
    401         if postgis:
     401        if SpatialBackend.postgis:
    402402            contains_mask = 'T*T***FF*'
    403403            within_mask = 'T*F**F***'
    404404            intersects_mask = 'T********'
    405         elif oracle:
     405        elif SpatialBackend.oracle:
    406406            contains_mask = 'contains'
    407407            within_mask = 'inside'
    408408            # TODO: This is not quite the same as the PostGIS mask above
     
    417417        self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, within_mask)).name)
    418418
    419419        # Testing intersection relation mask.
    420         if not oracle:
     420        if not SpatialBackend.oracle:
    421421            self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt1, intersects_mask)).name)
    422422            self.assertEqual('Texas', Country.objects.get(mpoly__relate=(pnt2, intersects_mask)).name)
    423423            self.assertEqual('Lawrence', City.objects.get(point__relate=(ks.poly, intersects_mask)).name)
     
    479479        "Testing the `centroid` GeoQuerySet method."
    480480        if DISABLE: return
    481481        qs = State.objects.exclude(poly__isnull=True).centroid()
    482         if oracle: tol = 0.1
     482        if SpatialBackend.oracle: tol = 0.1
    483483        else: tol = 0.000000001
    484484        for s in qs:
    485485            self.assertEqual(True, s.poly.centroid.equals_exact(s.centroid, tol))
     
    536536        for c in City.objects.filter(point__isnull=False).num_geom():
    537537            # Oracle will return 1 for the number of geometries on non-collections,
    538538            # whereas PostGIS will return None.
    539             if postgis: self.assertEqual(None, c.num_geom)
     539            if SpatialBackend.postgis: self.assertEqual(None, c.num_geom)
    540540            else: self.assertEqual(1, c.num_geom)
    541541
    542542    def test24_numpoints(self):
    543543        "Testing the `num_points` GeoQuerySet method."
    544544        if DISABLE: return
    545545        for c in Country.objects.num_points(): self.assertEqual(c.mpoly.num_points, c.num_points)
    546         if postgis:
     546        if SpatialBackend.postgis:
    547547            # Oracle cannot count vertices in Point geometries.
    548548            for c in City.objects.num_points(): self.assertEqual(1, c.num_points)
    549549
     
    558558            self.assertEqual(c.mpoly.sym_difference(geom), c.sym_difference)
    559559            self.assertEqual(c.mpoly.union(geom), c.union)
    560560
     561    def test26_inherited_geofields(self):
     562        "Test GeoQuerySet methods on inherited Geometry fields."
     563        # Creating a Pennsylvanian city.
     564        mansfield = PennsylvaniaCity.objects.create(name='Mansfield', county='Tioga', point='POINT(-77.071445 41.823881)')
     565
     566        # All transformation SQL will need to be performed on the
     567        # _parent_ table.
     568        qs = PennsylvaniaCity.objects.transform(32128)
     569       
     570        self.assertEqual(1, qs.count())
     571        for pc in qs: self.assertEqual(32128, pc.point.srid)
     572
    561573from test_feeds import GeoFeedTest
    562574from test_sitemaps import GeoSitemapTest
    563575def suite():
  • django/contrib/gis/tests/geoapp/models.py

     
    1616    objects = models.GeoManager()
    1717    def __unicode__(self): return self.name
    1818
     19# This is an inherited model from City
     20class PennsylvaniaCity(City):
     21    county = models.CharField(max_length=30)
     22    objects = models.GeoManager() # TODO: This should be implicitly inherited.
     23
    1924class State(models.Model):
    2025    name = models.CharField(max_length=30)
    2126    poly = models.PolygonField(null=null_flag) # Allowing NULL geometries here.
Back to Top