67 lines
2.8 KiB
Python
67 lines
2.8 KiB
Python
import datetime
|
|
from django.db.models.fields import Field
|
|
from django.db.models.sql.where import WhereNode
|
|
from django.contrib.gis.db.backend import get_geo_where_clause, SpatialBackend
|
|
|
|
class GeoAnnotation(object):
|
|
"""
|
|
The annotation used for GeometryFields; basically a placeholder
|
|
for metadata needed by the `get_geo_where_clause` of the spatial
|
|
backend.
|
|
"""
|
|
def __init__(self, field, value, where):
|
|
self.geodetic = field.geodetic
|
|
self.geom_type = field._geom
|
|
self.value = value
|
|
self.where = tuple(where)
|
|
|
|
class GeoWhereNode(WhereNode):
|
|
"""
|
|
Used to represent the SQL where-clause for spatial databases --
|
|
these are tied to the GeoQuery class that created it.
|
|
"""
|
|
def add(self, data, connector):
|
|
"""
|
|
This is overridden from the regular WhereNode to handle the
|
|
peculiarties of GeometryFields, because they need a special
|
|
annotation object that contains the spatial metadata from the
|
|
field to generate the spatial SQL.
|
|
"""
|
|
if not isinstance(data, (list, tuple)):
|
|
return super(WhereNode, self).add(data, connector)
|
|
|
|
obj, lookup_type, value = data
|
|
alias, col, field = obj.alias, obj.col, obj.field
|
|
|
|
if not hasattr(field, "_geom"):
|
|
# Not a geographic field, so call `WhereNode.add`.
|
|
return super(GeoWhereNode, self).add(data, connector)
|
|
else:
|
|
# `GeometryField.get_db_prep_lookup` returns a where clause
|
|
# substitution array in addition to the parameters.
|
|
where, params = field.get_db_prep_lookup(lookup_type, value)
|
|
|
|
# The annotation will be a `GeoAnnotation` object that
|
|
# will contain the necessary geometry field metadata for
|
|
# the `get_geo_where_clause` to construct the appropriate
|
|
# spatial SQL when `make_atom` is called.
|
|
annotation = GeoAnnotation(field, value, where)
|
|
return super(WhereNode, self).add((obj, lookup_type, annotation, params), connector)
|
|
|
|
def make_atom(self, child, qn):
|
|
lvalue, lookup_type, value_annot, params = child
|
|
|
|
if isinstance(value_annot, GeoAnnotation):
|
|
if lookup_type in SpatialBackend.gis_terms:
|
|
# Getting the geographic where clause; substitution parameters
|
|
# will be populated in the GeoFieldSQL object returned by the
|
|
# GeometryField.
|
|
gwc = get_geo_where_clause(lvalue.alias, lvalue.col, lookup_type, value_annot)
|
|
return gwc % value_annot.where, params
|
|
else:
|
|
raise TypeError('Invalid lookup type: %r' % lookup_type)
|
|
else:
|
|
# If not a GeometryField, call the `make_atom` from the
|
|
# base class.
|
|
return super(GeoWhereNode, self).make_atom(child, qn)
|