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) alias, col, field, lookup_type, value = data 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((alias, col, field.db_type(), lookup_type, annotation, params), connector) def make_atom(self, child, qn): table_alias, name, db_type, 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(table_alias, name, 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)