You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

proxy.py 3.1KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. """
  2. The SpatialProxy object allows for lazy-geometries and lazy-rasters. The proxy
  3. uses Python descriptors for instantiating and setting Geometry or Raster
  4. objects corresponding to geographic model fields.
  5. Thanks to Robert Coup for providing this functionality (see #4322).
  6. """
  7. from django.db.models.query_utils import DeferredAttribute
  8. class SpatialProxy(DeferredAttribute):
  9. def __init__(self, klass, field, load_func=None):
  10. """
  11. Initialize on the given Geometry or Raster class (not an instance)
  12. and the corresponding field.
  13. """
  14. self._field = field
  15. self._klass = klass
  16. self._load_func = load_func or klass
  17. super().__init__(field.attname)
  18. def __get__(self, instance, cls=None):
  19. """
  20. Retrieve the geometry or raster, initializing it using the
  21. corresponding class specified during initialization and the value of
  22. the field. Currently, GEOS or OGR geometries as well as GDALRasters are
  23. supported.
  24. """
  25. if instance is None:
  26. # Accessed on a class, not an instance
  27. return self
  28. # Getting the value of the field.
  29. try:
  30. geo_value = instance.__dict__[self._field.attname]
  31. except KeyError:
  32. geo_value = super().__get__(instance, cls)
  33. if isinstance(geo_value, self._klass):
  34. geo_obj = geo_value
  35. elif (geo_value is None) or (geo_value == ''):
  36. geo_obj = None
  37. else:
  38. # Otherwise, a geometry or raster object is built using the field's
  39. # contents, and the model's corresponding attribute is set.
  40. geo_obj = self._load_func(geo_value)
  41. setattr(instance, self._field.attname, geo_obj)
  42. return geo_obj
  43. def __set__(self, instance, value):
  44. """
  45. Retrieve the proxied geometry or raster with the corresponding class
  46. specified during initialization.
  47. To set geometries, use values of None, HEXEWKB, or WKT.
  48. To set rasters, use JSON or dict values.
  49. """
  50. # The geographic type of the field.
  51. gtype = self._field.geom_type
  52. if gtype == 'RASTER' and (value is None or isinstance(value, (str, dict, self._klass))):
  53. # For raster fields, assure input is None or a string, dict, or
  54. # raster instance.
  55. pass
  56. elif isinstance(value, self._klass):
  57. # The geometry type must match that of the field -- unless the
  58. # general GeometryField is used.
  59. if value.srid is None:
  60. # Assigning the field SRID if the geometry has no SRID.
  61. value.srid = self._field.srid
  62. elif value is None or isinstance(value, (str, memoryview)):
  63. # Set geometries with None, WKT, HEX, or WKB
  64. pass
  65. else:
  66. raise TypeError('Cannot set %s SpatialProxy (%s) with value of type: %s' % (
  67. instance.__class__.__name__, gtype, type(value)))
  68. # Setting the objects dictionary with the value, and returning.
  69. instance.__dict__[self._field.attname] = value
  70. return value