Removed GeoWhereNode, obsoleted by GISLookup
This commit is contained in:
parent
b8fc167b32
commit
3caf957ed5
|
@ -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
|
||||||
|
|
|
@ -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',
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
|
Loading…
Reference in New Issue