fields.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import inspect
  2. import functools
  3. import mongoengine as me
  4. from marshmallow_mongoengine import fields as ma_fields
  5. from marshmallow_mongoengine.conversion import params
  6. from marshmallow_mongoengine.exceptions import ModelConversionError
  7. class MetaFieldBuilder(object):
  8. """
  9. Convert a given Mongoengine Field to an equivalent Marshmallow Field
  10. """
  11. BASE_AVAILABLE_PARAMS = (params.DescriptionParam, params.AllowNoneParam,
  12. params.ChoiceParam, params.RequiredParam)
  13. AVAILABLE_PARAMS = ()
  14. MARSHMALLOW_FIELD_CLS = None
  15. def __init__(self, field):
  16. self.mongoengine_field = field
  17. self.params = [paramCls(field)
  18. for paramCls in self.BASE_AVAILABLE_PARAMS + self.AVAILABLE_PARAMS]
  19. def build_marshmallow_field(self, **kwargs):
  20. """
  21. :return: The Marshmallow Field instanciated and configured
  22. """
  23. field_kwargs = None
  24. for param in self.params:
  25. field_kwargs = param.apply(field_kwargs)
  26. field_kwargs.update(kwargs)
  27. return self.marshmallow_field_cls(**field_kwargs)
  28. def _get_marshmallow_field_cls(self):
  29. """
  30. Return the marshmallow Field class, overload this method to
  31. generate more dynamic field class
  32. """
  33. return self.MARSHMALLOW_FIELD_CLS
  34. @property
  35. def marshmallow_field_cls(self):
  36. return self._get_marshmallow_field_cls()
  37. class ListBuilder(MetaFieldBuilder):
  38. AVAILABLE_PARAMS = (params.LenghtParam,)
  39. MARSHMALLOW_FIELD_CLS = ma_fields.List
  40. def _get_marshmallow_field_cls(self):
  41. sub_field = get_field_builder_for_data_type(
  42. self.mongoengine_field.field)
  43. return functools.partial(
  44. self.MARSHMALLOW_FIELD_CLS,
  45. sub_field.build_marshmallow_field()
  46. )
  47. class ReferenceBuilder(MetaFieldBuilder):
  48. AVAILABLE_PARAMS = ()
  49. MARSHMALLOW_FIELD_CLS = ma_fields.Reference
  50. def _get_marshmallow_field_cls(self):
  51. return functools.partial(
  52. self.MARSHMALLOW_FIELD_CLS,
  53. self.mongoengine_field.document_type
  54. )
  55. class GenericReferenceBuilder(MetaFieldBuilder):
  56. BASE_AVAILABLE_PARAMS = tuple([p for p in MetaFieldBuilder.BASE_AVAILABLE_PARAMS
  57. if p is not params.ChoiceParam])
  58. AVAILABLE_PARAMS = ()
  59. MARSHMALLOW_FIELD_CLS = ma_fields.GenericReference
  60. def build_marshmallow_field(self, **kwargs):
  61. # Special handle for the choice field given it represent the
  62. # reference's document class
  63. kwargs['choices'] = getattr(self.mongoengine_field, 'choices', None)
  64. return super(GenericReferenceBuilder, self).build_marshmallow_field(**kwargs)
  65. class EmbeddedDocumentBuilder(MetaFieldBuilder):
  66. AVAILABLE_PARAMS = ()
  67. MARSHMALLOW_FIELD_CLS = ma_fields.Nested
  68. BASE_NESTED_SCHEMA_CLS = None
  69. def _get_marshmallow_field_cls(self):
  70. # Recursive build of marshmallow schema
  71. from marshmallow_mongoengine.schema import ModelSchema
  72. base_nested_schema_cls = self.BASE_NESTED_SCHEMA_CLS or ModelSchema
  73. class NestedSchema(base_nested_schema_cls):
  74. class Meta:
  75. model = self.mongoengine_field.document_type
  76. return functools.partial(
  77. self.MARSHMALLOW_FIELD_CLS,
  78. NestedSchema
  79. )
  80. class MapBuilder(MetaFieldBuilder):
  81. AVAILABLE_PARAMS = ()
  82. MARSHMALLOW_FIELD_CLS = ma_fields.Map
  83. def _get_marshmallow_field_cls(self):
  84. # Recursive build of marshmallow schema
  85. from marshmallow_mongoengine.convert import convert_field
  86. return functools.partial(
  87. self.MARSHMALLOW_FIELD_CLS,
  88. convert_field(self.mongoengine_field.field)
  89. )
  90. def get_field_builder_for_data_type(field_me):
  91. field_me_types = inspect.getmro(type(field_me))
  92. for field_me_type in field_me_types:
  93. if field_me_type in FIELD_MAPPING:
  94. field_ma_cls = FIELD_MAPPING[field_me_type]
  95. break
  96. else:
  97. raise ModelConversionError(
  98. 'Could not find field of type {0}.'.format(field_me))
  99. return field_ma_cls(field_me)
  100. FIELD_MAPPING = {
  101. }
  102. def register_field_builder(mongo_field_cls, builder):
  103. """
  104. Register a :class MetaFieldBuilder: to a given Mongoengine Field
  105. :param mongo_field_cls: Mongoengine Field
  106. :param build: field_builder to register
  107. """
  108. FIELD_MAPPING[mongo_field_cls] = builder
  109. def register_field(mongo_field_cls, marshmallow_field_cls,
  110. available_params=()):
  111. """
  112. Bind a marshmallow field to it corresponding mongoengine field
  113. :param mongo_field_cls: Mongoengine Field
  114. :param marshmallow_field_cls: Marshmallow Field
  115. :param available_params: List of :class marshmallow_mongoengine.cnoversion.params.MetaParam:
  116. instances to import the mongoengine field config to marshmallow
  117. """
  118. class Builder(MetaFieldBuilder):
  119. AVAILABLE_PARAMS = available_params
  120. MARSHMALLOW_FIELD_CLS = marshmallow_field_cls
  121. register_field_builder(mongo_field_cls, Builder)
  122. register_field(me.fields.BinaryField, ma_fields.Integer,
  123. available_params=(params.SizeParam,))
  124. register_field(me.fields.BooleanField, ma_fields.Boolean)
  125. register_field(me.fields.ComplexDateTimeField, ma_fields.DateTime)
  126. register_field(me.fields.DateTimeField, ma_fields.DateTime)
  127. register_field(me.fields.DecimalField, ma_fields.Decimal,
  128. available_params=(params.SizeParam, params.PrecisionParam))
  129. register_field(me.fields.DictField, ma_fields.Raw)
  130. register_field(me.fields.DynamicField, ma_fields.Raw)
  131. register_field(me.fields.EmailField, ma_fields.Email,
  132. available_params=(params.LenghtParam,))
  133. register_field(me.fields.FloatField, ma_fields.Float,
  134. available_params=(params.SizeParam,))
  135. register_field(me.fields.GenericEmbeddedDocumentField,
  136. ma_fields.GenericEmbeddedDocument)
  137. register_field_builder(me.fields.GenericReferenceField, GenericReferenceBuilder)
  138. register_field_builder(me.fields.ReferenceField, ReferenceBuilder)
  139. # FilesField and ImageField can't be simply displayed...
  140. register_field(me.fields.FileField, ma_fields.Skip)
  141. register_field(me.fields.ImageField, ma_fields.Skip)
  142. register_field(me.fields.IntField, ma_fields.Integer,
  143. available_params=(params.SizeParam,))
  144. register_field(me.fields.LongField, ma_fields.Integer,
  145. available_params=(params.SizeParam,))
  146. register_field(me.fields.ObjectIdField, ma_fields.ObjectId)
  147. register_field(me.fields.UUIDField, ma_fields.UUID)
  148. register_field(me.fields.PointField, ma_fields.Point)
  149. register_field(me.fields.SequenceField, ma_fields.Integer,
  150. available_params=(params.SizeParam,)) # TODO: handle value_decorator
  151. register_field(me.fields.StringField, ma_fields.String,
  152. available_params=(params.LenghtParam, params.RegexParam))
  153. register_field(me.fields.URLField, ma_fields.URL,
  154. available_params=(params.LenghtParam,))
  155. register_field_builder(me.fields.EmbeddedDocumentField, EmbeddedDocumentBuilder)
  156. register_field_builder(me.fields.ListField, ListBuilder)
  157. register_field_builder(me.fields.MapField, MapBuilder)
  158. register_field_builder(me.fields.SortedListField, ListBuilder)
  159. # TODO: finish fields...
  160. # me.fields.GeoPointField: ma_fields.GeoPoint,
  161. # me.fields.LineStringField: ma_fields.LineString,
  162. # me.fields.PolygonField: ma_fields.Polygon,
  163. # me.fields.MultiPointField: ma_fields.MultiPoint,
  164. # me.fields.MultiLineStringField: ma_fields.MultiLineString,
  165. # me.fields.MultiPolygonField: ma_fields.MultiPolygon,