Removed GeoWhereNode, obsoleted by GISLookup

This commit is contained in:
Claude Paroz 2014-05-24 16:18:45 +02:00
parent b8fc167b32
commit 3caf957ed5
4 changed files with 49 additions and 103 deletions

View File

@ -1,10 +1,51 @@
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import FieldDoesNotExist
from django.db.models.lookups import Lookup from django.db.models.lookups import Lookup
from django.db.models.sql.expressions import SQLEvaluator from django.db.models.sql.expressions import SQLEvaluator
class GISLookup(Lookup): class GISLookup(Lookup):
@classmethod
def _check_geo_field(cls, opts, lookup):
"""
Utility for checking the given lookup with the given model options.
The lookup is a string either specifying the geographic field, e.g.
'point, 'the_geom', or a related lookup on a geographic field like
'address__point'.
If a GeometryField exists according to the given lookup on the model
options, it will be returned. Otherwise returns None.
"""
from django.contrib.gis.db.models.fields import GeometryField
# This takes into account the situation where the lookup is a
# lookup to a related geographic field, e.g., 'address__point'.
field_list = lookup.split(LOOKUP_SEP)
# Reversing so list operates like a queue of related lookups,
# and popping the top lookup.
field_list.reverse()
fld_name = field_list.pop()
try:
geo_fld = opts.get_field(fld_name)
# If the field list is still around, then it means that the
# lookup was for a geometry field across a relationship --
# thus we keep on getting the related model options and the
# model field associated with the next field in the list
# until there's no more left.
while len(field_list):
opts = geo_fld.rel.to._meta
geo_fld = opts.get_field(field_list.pop())
except (FieldDoesNotExist, AttributeError):
return False
# Finally, make sure we got a Geographic field and return.
if isinstance(geo_fld, GeometryField):
return geo_fld
else:
return False
def as_sql(self, qn, connection): def as_sql(self, qn, connection):
from django.contrib.gis.db.models.sql import GeoWhereNode
# We use the same approach as was used by GeoWhereNode. It would # We use the same approach as was used by GeoWhereNode. It would
# be a good idea to upgrade GIS to use similar code that is used # be a good idea to upgrade GIS to use similar code that is used
# for other lookups. # for other lookups.
@ -12,7 +53,7 @@ class GISLookup(Lookup):
# Make sure the F Expression destination field exists, and # Make sure the F Expression destination field exists, and
# set an `srid` attribute with the same as that of the # set an `srid` attribute with the same as that of the
# destination. # destination.
geo_fld = GeoWhereNode._check_geo_field(self.rhs.opts, self.rhs.expression.name) geo_fld = self._check_geo_field(self.rhs.opts, self.rhs.expression.name)
if not geo_fld: if not geo_fld:
raise ValueError('No geographic field found in expression.') raise ValueError('No geographic field found in expression.')
self.rhs.srid = geo_fld.srid self.rhs.srid = geo_fld.srid

View File

@ -1,7 +1,6 @@
from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField
from django.contrib.gis.db.models.sql.query import GeoQuery from django.contrib.gis.db.models.sql.query import GeoQuery
from django.contrib.gis.db.models.sql.where import GeoWhereNode
__all__ = [ __all__ = [
'AreaField', 'DistanceField', 'GeomField', 'GeoQuery', 'GeoWhereNode', 'AreaField', 'DistanceField', 'GeomField', 'GeoQuery',
] ]

View File

@ -3,9 +3,9 @@ from django.db.models.query import sql
from django.contrib.gis.db.models.constants import ALL_TERMS from django.contrib.gis.db.models.constants import ALL_TERMS
from django.contrib.gis.db.models.fields import GeometryField from django.contrib.gis.db.models.fields import GeometryField
from django.contrib.gis.db.models.lookups import GISLookup
from django.contrib.gis.db.models.sql import aggregates as gis_aggregates from django.contrib.gis.db.models.sql import aggregates as gis_aggregates
from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField from django.contrib.gis.db.models.sql.conversion import AreaField, DistanceField, GeomField
from django.contrib.gis.db.models.sql.where import GeoWhereNode
from django.contrib.gis.geometry.backend import Geometry from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Area, Distance from django.contrib.gis.measure import Area, Distance
@ -21,11 +21,10 @@ class GeoQuery(sql.Query):
compiler = 'GeoSQLCompiler' compiler = 'GeoSQLCompiler'
#### Methods overridden from the base Query class #### #### Methods overridden from the base Query class ####
def __init__(self, model, where=GeoWhereNode): def __init__(self, model):
super(GeoQuery, self).__init__(model, where) super(GeoQuery, self).__init__(model) #, where)
# The following attributes are customized for the GeoQuerySet. # The following attributes are customized for the GeoQuerySet.
# The GeoWhereNode and SpatialBackend classes contain backend-specific # The SpatialBackend classes contain backend-specific routines and functions.
# routines and functions.
self.custom_select = {} self.custom_select = {}
self.transformed_srid = None self.transformed_srid = None
self.extra_select_fields = {} self.extra_select_fields = {}
@ -108,4 +107,4 @@ class GeoQuery(sql.Query):
else: else:
# Otherwise, check by the given field name -- which may be # Otherwise, check by the given field name -- which may be
# a lookup to a _related_ geographic field. # a lookup to a _related_ geographic field.
return GeoWhereNode._check_geo_field(self.model._meta, field_name) return GISLookup._check_geo_field(self.model._meta, field_name)

View File

@ -1,93 +0,0 @@
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields import FieldDoesNotExist
from django.db.models.sql.expressions import SQLEvaluator
from django.db.models.sql.where import Constraint, WhereNode
from django.contrib.gis.db.models.fields import GeometryField
class GeoConstraint(Constraint):
"""
This subclass overrides `process` to better handle geographic SQL
construction.
"""
def __init__(self, init_constraint):
self.alias = init_constraint.alias
self.col = init_constraint.col
self.field = init_constraint.field
def process(self, lookup_type, value, connection):
if isinstance(value, SQLEvaluator):
# Make sure the F Expression destination field exists, and
# set an `srid` attribute with the same as that of the
# destination.
geo_fld = GeoWhereNode._check_geo_field(value.opts, value.expression.name)
if not geo_fld:
raise ValueError('No geographic field found in expression.')
value.srid = geo_fld.srid
db_type = self.field.db_type(connection=connection)
params = self.field.get_db_prep_lookup(lookup_type, value, connection=connection)
return (self.alias, self.col, db_type), params
class GeoWhereNode(WhereNode):
"""
Used to represent the SQL where-clause for spatial databases --
these are tied to the GeoQuery class that created it.
"""
def _prepare_data(self, data):
if isinstance(data, (list, tuple)):
obj, lookup_type, value = data
if (isinstance(obj, Constraint) and
isinstance(obj.field, GeometryField)):
data = (GeoConstraint(obj), lookup_type, value)
return super(GeoWhereNode, self)._prepare_data(data)
def make_atom(self, child, qn, connection):
lvalue, lookup_type, value_annot, params_or_value = child
if isinstance(lvalue, GeoConstraint):
data, params = lvalue.process(lookup_type, params_or_value, connection)
spatial_sql, spatial_params = connection.ops.spatial_lookup_sql(
data, lookup_type, params_or_value, lvalue.field, qn)
return spatial_sql, spatial_params + params
else:
return super(GeoWhereNode, self).make_atom(child, qn, connection)
@classmethod
def _check_geo_field(cls, opts, lookup):
"""
Utility for checking the given lookup with the given model options.
The lookup is a string either specifying the geographic field, e.g.
'point, 'the_geom', or a related lookup on a geographic field like
'address__point'.
If a GeometryField exists according to the given lookup on the model
options, it will be returned. Otherwise returns None.
"""
# This takes into account the situation where the lookup is a
# lookup to a related geographic field, e.g., 'address__point'.
field_list = lookup.split(LOOKUP_SEP)
# Reversing so list operates like a queue of related lookups,
# and popping the top lookup.
field_list.reverse()
fld_name = field_list.pop()
try:
geo_fld = opts.get_field(fld_name)
# If the field list is still around, then it means that the
# lookup was for a geometry field across a relationship --
# thus we keep on getting the related model options and the
# model field associated with the next field in the list
# until there's no more left.
while len(field_list):
opts = geo_fld.rel.to._meta
geo_fld = opts.get_field(field_list.pop())
except (FieldDoesNotExist, AttributeError):
return False
# Finally, make sure we got a Geographic field and return.
if isinstance(geo_fld, GeometryField):
return geo_fld
else:
return False