Ticket #10159: geowherenode_expressions_fix_v2.diff

File geowherenode_expressions_fix_v2.diff, 8.1 KB (added by jbronn, 7 years ago)

Fix, tests, and some cleanup.

  • django/contrib/gis/db/models/sql/where.py

     
    11import datetime
     2from django.db import connection
    23from django.db.models.fields import Field
     4from django.db.models.sql.expressions import SQLEvaluator
    35from django.db.models.sql.where import WhereNode
    46from django.contrib.gis.db.backend import get_geo_where_clause, SpatialBackend
     7qn = connection.ops.quote_name
    58
    69class GeoAnnotation(object):
    710    """
     
    3740            # Not a geographic field, so call `WhereNode.add`.
    3841            return super(GeoWhereNode, self).add(data, connector)
    3942        else:
    40             # `GeometryField.get_db_prep_lookup` returns a where clause
    41             # substitution array in addition to the parameters.
    42             where, params = field.get_db_prep_lookup(lookup_type, value)
    4343
     44            if isinstance(value, SQLEvaluator):
     45                # If an expression is used, we are getting a database column so
     46                # we we don't send to get_db_prep_lookup.
     47                where =  ['%s.%s' % tuple(map(qn, value.cols[value.expression]))]
     48                params = ()
     49            else:
     50                # `GeometryField.get_db_prep_lookup` returns a where clause
     51                # substitution array in addition to the parameters.
     52                where, params = field.get_db_prep_lookup(lookup_type, value)
     53
    4454            # The annotation will be a `GeoAnnotation` object that
    4555            # will contain the necessary geometry field metadata for
    4656            # the `get_geo_where_clause` to construct the appropriate
  • django/contrib/gis/tests/relatedapp/tests.py

     
    11import os, unittest
    2 from django.contrib.gis.geos import *
     2from django.contrib.gis.db.models import F, Extent, Union
     3from django.contrib.gis.geos import GEOSGeometry, Point
    34from django.contrib.gis.tests.utils import no_mysql, postgis
    45from django.conf import settings
    5 from models import City, Location, DirectoryEntry
     6from models import City, Location, DirectoryEntry, Parcel
    67
    78cities = (('Aurora', 'TX', -97.516111, 33.058333),
    89          ('Roswell', 'NM', -104.528056, 33.387222),
     
    3940        # US Survey Feet (thus a tolerance of 0 implies error w/in 1 survey foot).
    4041        if postgis:
    4142            tol = 3
    42             nqueries = 4 # +1 for `postgis_lib_version`
    4343        else:
    4444            tol = 0
    45             nqueries = 3
    4645           
    4746        def check_pnt(ref, pnt):
    4847            self.assertAlmostEqual(ref.x, pnt.x, tol)
    4948            self.assertAlmostEqual(ref.y, pnt.y, tol)
    5049            self.assertEqual(ref.srid, pnt.srid)
    5150
    52         # Turning on debug so we can manually verify the number of SQL queries issued.
    53         # DISABLED: the number of queries count testing mechanism is way too brittle.
    54         #dbg = settings.DEBUG
    55         #settings.DEBUG = True
    56         from django.db import connection
    57 
    5851        # Each city transformed to the SRID of their state plane coordinate system.
    5952        transformed = (('Kecksburg', 2272, 'POINT(1490553.98959621 314792.131023984)'),
    6053                       ('Roswell', 2257, 'POINT(481902.189077221 868477.766629735)'),
     
    6558            # Doing this implicitly sets `select_related` select the location.
    6659            qs = list(City.objects.filter(name=name).transform(srid, field_name='location__point'))
    6760            check_pnt(GEOSGeometry(wkt, srid), qs[0].location.point)
    68         #settings.DEBUG= dbg
    6961
    70         # Verifying the number of issued SQL queries.
    71         #self.assertEqual(nqueries, len(connection.queries))
    72 
    7362    @no_mysql
    7463    def test04_related_aggregate(self):
    7564        "Testing the `extent` and `unionagg` GeoQuerySet aggregates on related geographic models."
    76         if postgis:
    77             # One for all locations, one that excludes Roswell.
    78             all_extent = (-104.528060913086, 33.0583305358887,-79.4607315063477, 40.1847610473633)
    79             txpa_extent = (-97.51611328125, 33.0583305358887,-79.4607315063477, 40.1847610473633)
    80             e1 = City.objects.extent(field_name='location__point')
    81             e2 = City.objects.exclude(name='Roswell').extent(field_name='location__point')
    82             for ref, e in [(all_extent, e1), (txpa_extent, e2)]:
    83                 for ref_val, e_val in zip(ref, e): self.assertAlmostEqual(ref_val, e_val)
    8465
     66        # This combines the Extent and Union aggregates into one query
     67        aggs = City.objects.aggregate(Extent('location__point'), Union('location__point'))
     68
     69        # One for all locations, one that excludes Roswell.
     70        all_extent = (-104.528060913086, 33.0583305358887,-79.4607315063477, 40.1847610473633)
     71        txpa_extent = (-97.51611328125, 33.0583305358887,-79.4607315063477, 40.1847610473633)
     72        e1 = City.objects.extent(field_name='location__point')
     73        e2 = City.objects.exclude(name='Roswell').extent(field_name='location__point')
     74        e3 = aggs['location__point__extent']
     75
     76        for ref, e in [(all_extent, e1), (txpa_extent, e2)]:
     77            for ref_val, e_val in zip(ref, e): self.assertAlmostEqual(ref_val, e_val)
     78
    8579        # The second union is for a query that has something in the WHERE clause.
    8680        ref_u1 = GEOSGeometry('MULTIPOINT(-104.528056 33.387222,-97.516111 33.058333,-79.460734 40.18476)', 4326)
    8781        ref_u2 = GEOSGeometry('MULTIPOINT(-97.516111 33.058333,-79.460734 40.18476)', 4326)
    8882        u1 = City.objects.unionagg(field_name='location__point')
    8983        u2 = City.objects.exclude(name='Roswell').unionagg(field_name='location__point')
     84        u3 = aggs['location__point__union']
     85
    9086        self.assertEqual(ref_u1, u1)
    9187        self.assertEqual(ref_u2, u2)
     88        self.assertEqual(ref_u1, u3)
    9289       
    9390    def test05_select_related_fk_to_subclass(self):
    9491        "Testing that calling select_related on a query over a model with an FK to a model subclass works"
    9592        # Regression test for #9752.
    9693        l = list(DirectoryEntry.objects.all().select_related())
    9794
     95    def test6_f_expressions(self):
     96        "Testing F() expressions on Geometry fields."
     97        # Constructing a dummy parcel border and getting the City FK
     98        p1_border = GEOSGeometry('POLYGON((-97.501205 33.052520,-97.501205 33.052576,-97.501150 33.052576,-97.501150 33.052520,-97.501205 33.052520))')
     99        p1_city = City.objects.get(name='Aurora')
     100       
     101        # First parcel has incorrect center point that is equal to the City point.
     102        p1 = Parcel.objects.create(name='P1', city=p1_city, center=p1_city.location.point, border=p1_border)
     103        p2 = Parcel.objects.create(name='P2', city=p1_city, center=p1_border.centroid, border=p1_border)
     104
     105        # Should return the second Parcel, which has the center within the
     106        # border.
     107        qs = Parcel.objects.filter(center__within=F('border'))
     108        self.assertEqual(1, len(qs))
     109        self.assertEqual('P2', qs[0].name)
     110
     111        # Should return the first Parcel, which has the center point equal
     112        # to the point in the City ForeignKey.
     113        qs = Parcel.objects.filter(center=F('city__location__point'))
     114        self.assertEqual(1, len(qs))
     115        self.assertEqual('P1', qs[0].name)
     116
    98117    # TODO: Related tests for KML, GML, and distance lookups.
    99118       
    100119def suite():
  • django/contrib/gis/tests/relatedapp/models.py

     
    2020    listing_text = models.CharField(max_length=50)
    2121    location = models.ForeignKey(AugmentedLocation)
    2222    objects = models.GeoManager()
     23
     24class Parcel(models.Model):
     25    name = models.CharField(max_length=30)
     26    city = models.ForeignKey(City)
     27    center = models.PointField()
     28    border = models.PolygonField()
     29    objects = models.GeoManager()
     30    def __unicode__(self): return self.name
Back to Top