app/django/contrib/gis/db/models/sql/where.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     1 import datetime
       
     2 from django.db.models.fields import Field
       
     3 from django.db.models.sql.where import WhereNode
       
     4 from django.contrib.gis.db.backend import get_geo_where_clause, SpatialBackend
       
     5 
       
     6 class GeoAnnotation(object):
       
     7     """
       
     8     The annotation used for GeometryFields; basically a placeholder
       
     9     for metadata needed by the `get_geo_where_clause` of the spatial
       
    10     backend.
       
    11     """
       
    12     def __init__(self, field, value, where):
       
    13         self.geodetic = field.geodetic
       
    14         self.geom_type = field._geom
       
    15         self.value = value
       
    16         self.where = tuple(where)
       
    17 
       
    18 class GeoWhereNode(WhereNode):
       
    19     """
       
    20     Used to represent the SQL where-clause for spatial databases --
       
    21     these are tied to the GeoQuery class that created it.
       
    22     """
       
    23     def add(self, data, connector):
       
    24         """
       
    25         This is overridden from the regular WhereNode to handle the 
       
    26         peculiarties of GeometryFields, because they need a special 
       
    27         annotation object that contains the spatial metadata from the 
       
    28         field to generate the spatial SQL.
       
    29         """
       
    30         if not isinstance(data, (list, tuple)):
       
    31             return super(WhereNode, self).add(data, connector)
       
    32         alias, col, field, lookup_type, value = data     
       
    33         if not hasattr(field, "_geom"):
       
    34             # Not a geographic field, so call `WhereNode.add`.
       
    35             return super(GeoWhereNode, self).add(data, connector)
       
    36         else:
       
    37             # `GeometryField.get_db_prep_lookup` returns a where clause
       
    38             # substitution array in addition to the parameters.
       
    39             where, params = field.get_db_prep_lookup(lookup_type, value)
       
    40 
       
    41             # The annotation will be a `GeoAnnotation` object that
       
    42             # will contain the necessary geometry field metadata for
       
    43             # the `get_geo_where_clause` to construct the appropriate
       
    44             # spatial SQL when `make_atom` is called.
       
    45             annotation = GeoAnnotation(field, value, where)
       
    46             return super(WhereNode, self).add((alias, col, field.db_type(), lookup_type,
       
    47                                                annotation, params), connector)
       
    48 
       
    49     def make_atom(self, child, qn):
       
    50         table_alias, name, db_type, lookup_type, value_annot, params = child
       
    51  
       
    52         if isinstance(value_annot, GeoAnnotation):
       
    53             if lookup_type in SpatialBackend.gis_terms:
       
    54                 # Getting the geographic where clause; substitution parameters
       
    55                 # will be populated in the GeoFieldSQL object returned by the
       
    56                 # GeometryField.
       
    57                 gwc = get_geo_where_clause(table_alias, name, lookup_type, value_annot)
       
    58                 return gwc % value_annot.where, params
       
    59             else:
       
    60                 raise TypeError('Invalid lookup type: %r' % lookup_type)
       
    61         else:
       
    62             # If not a GeometryField, call the `make_atom` from the 
       
    63             # base class.
       
    64             return super(GeoWhereNode, self).make_atom(child, qn)