/django/contrib/gis/db/models/sql/where.py

https://code.google.com/p/mango-py/ · Python · 89 lines · 65 code · 7 blank · 17 comment · 11 complexity · 1aa4846fe64700a94cca0806b49d0948 MD5 · raw file

  1. from django.db.models.fields import Field, FieldDoesNotExist
  2. from django.db.models.sql.constants import LOOKUP_SEP
  3. from django.db.models.sql.expressions import SQLEvaluator
  4. from django.db.models.sql.where import Constraint, WhereNode
  5. from django.contrib.gis.db.models.fields import GeometryField
  6. class GeoConstraint(Constraint):
  7. """
  8. This subclass overrides `process` to better handle geographic SQL
  9. construction.
  10. """
  11. def __init__(self, init_constraint):
  12. self.alias = init_constraint.alias
  13. self.col = init_constraint.col
  14. self.field = init_constraint.field
  15. def process(self, lookup_type, value, connection):
  16. if isinstance(value, SQLEvaluator):
  17. # Make sure the F Expression destination field exists, and
  18. # set an `srid` attribute with the same as that of the
  19. # destination.
  20. geo_fld = GeoWhereNode._check_geo_field(value.opts, value.expression.name)
  21. if not geo_fld:
  22. raise ValueError('No geographic field found in expression.')
  23. value.srid = geo_fld.srid
  24. db_type = self.field.db_type(connection=connection)
  25. params = self.field.get_db_prep_lookup(lookup_type, value, connection=connection)
  26. return (self.alias, self.col, db_type), params
  27. class GeoWhereNode(WhereNode):
  28. """
  29. Used to represent the SQL where-clause for spatial databases --
  30. these are tied to the GeoQuery class that created it.
  31. """
  32. def add(self, data, connector):
  33. if isinstance(data, (list, tuple)):
  34. obj, lookup_type, value = data
  35. if ( isinstance(obj, Constraint) and
  36. isinstance(obj.field, GeometryField) ):
  37. data = (GeoConstraint(obj), lookup_type, value)
  38. super(GeoWhereNode, self).add(data, connector)
  39. def make_atom(self, child, qn, connection):
  40. lvalue, lookup_type, value_annot, params_or_value = child
  41. if isinstance(lvalue, GeoConstraint):
  42. data, params = lvalue.process(lookup_type, params_or_value, connection)
  43. spatial_sql = connection.ops.spatial_lookup_sql(data, lookup_type, params_or_value, lvalue.field, qn)
  44. return spatial_sql, params
  45. else:
  46. return super(GeoWhereNode, self).make_atom(child, qn, connection)
  47. @classmethod
  48. def _check_geo_field(cls, opts, lookup):
  49. """
  50. Utility for checking the given lookup with the given model options.
  51. The lookup is a string either specifying the geographic field, e.g.
  52. 'point, 'the_geom', or a related lookup on a geographic field like
  53. 'address__point'.
  54. If a GeometryField exists according to the given lookup on the model
  55. options, it will be returned. Otherwise returns None.
  56. """
  57. # This takes into account the situation where the lookup is a
  58. # lookup to a related geographic field, e.g., 'address__point'.
  59. field_list = lookup.split(LOOKUP_SEP)
  60. # Reversing so list operates like a queue of related lookups,
  61. # and popping the top lookup.
  62. field_list.reverse()
  63. fld_name = field_list.pop()
  64. try:
  65. geo_fld = opts.get_field(fld_name)
  66. # If the field list is still around, then it means that the
  67. # lookup was for a geometry field across a relationship --
  68. # thus we keep on getting the related model options and the
  69. # model field associated with the next field in the list
  70. # until there's no more left.
  71. while len(field_list):
  72. opts = geo_fld.rel.to._meta
  73. geo_fld = opts.get_field(field_list.pop())
  74. except (FieldDoesNotExist, AttributeError):
  75. return False
  76. # Finally, make sure we got a Geographic field and return.
  77. if isinstance(geo_fld, GeometryField):
  78. return geo_fld
  79. else:
  80. return False