|
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) |