123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- import inspect
- import functools
- import mongoengine as me
- from marshmallow_mongoengine import fields as ma_fields
- from marshmallow_mongoengine.conversion import params
- from marshmallow_mongoengine.exceptions import ModelConversionError
- class MetaFieldBuilder(object):
- """
- Convert a given Mongoengine Field to an equivalent Marshmallow Field
- """
- BASE_AVAILABLE_PARAMS = (params.DescriptionParam, params.AllowNoneParam,
- params.ChoiceParam, params.RequiredParam)
- AVAILABLE_PARAMS = ()
- MARSHMALLOW_FIELD_CLS = None
- def __init__(self, field):
- self.mongoengine_field = field
- self.params = [paramCls(field)
- for paramCls in self.BASE_AVAILABLE_PARAMS + self.AVAILABLE_PARAMS]
- def build_marshmallow_field(self, **kwargs):
- """
- :return: The Marshmallow Field instanciated and configured
- """
- field_kwargs = None
- for param in self.params:
- field_kwargs = param.apply(field_kwargs)
- field_kwargs.update(kwargs)
- return self.marshmallow_field_cls(**field_kwargs)
- def _get_marshmallow_field_cls(self):
- """
- Return the marshmallow Field class, overload this method to
- generate more dynamic field class
- """
- return self.MARSHMALLOW_FIELD_CLS
- @property
- def marshmallow_field_cls(self):
- return self._get_marshmallow_field_cls()
- class ListBuilder(MetaFieldBuilder):
- AVAILABLE_PARAMS = (params.LenghtParam,)
- MARSHMALLOW_FIELD_CLS = ma_fields.List
- def _get_marshmallow_field_cls(self):
- sub_field = get_field_builder_for_data_type(
- self.mongoengine_field.field)
- return functools.partial(
- self.MARSHMALLOW_FIELD_CLS,
- sub_field.build_marshmallow_field()
- )
- class ReferenceBuilder(MetaFieldBuilder):
- AVAILABLE_PARAMS = ()
- MARSHMALLOW_FIELD_CLS = ma_fields.Reference
- def _get_marshmallow_field_cls(self):
- return functools.partial(
- self.MARSHMALLOW_FIELD_CLS,
- self.mongoengine_field.document_type
- )
- class GenericReferenceBuilder(MetaFieldBuilder):
- BASE_AVAILABLE_PARAMS = tuple([p for p in MetaFieldBuilder.BASE_AVAILABLE_PARAMS
- if p is not params.ChoiceParam])
- AVAILABLE_PARAMS = ()
- MARSHMALLOW_FIELD_CLS = ma_fields.GenericReference
- def build_marshmallow_field(self, **kwargs):
- # Special handle for the choice field given it represent the
- # reference's document class
- kwargs['choices'] = getattr(self.mongoengine_field, 'choices', None)
- return super(GenericReferenceBuilder, self).build_marshmallow_field(**kwargs)
- class EmbeddedDocumentBuilder(MetaFieldBuilder):
- AVAILABLE_PARAMS = ()
- MARSHMALLOW_FIELD_CLS = ma_fields.Nested
- BASE_NESTED_SCHEMA_CLS = None
- def _get_marshmallow_field_cls(self):
- # Recursive build of marshmallow schema
- from marshmallow_mongoengine.schema import ModelSchema
- base_nested_schema_cls = self.BASE_NESTED_SCHEMA_CLS or ModelSchema
- class NestedSchema(base_nested_schema_cls):
- class Meta:
- model = self.mongoengine_field.document_type
- return functools.partial(
- self.MARSHMALLOW_FIELD_CLS,
- NestedSchema
- )
- class MapBuilder(MetaFieldBuilder):
- AVAILABLE_PARAMS = ()
- MARSHMALLOW_FIELD_CLS = ma_fields.Map
- def _get_marshmallow_field_cls(self):
- # Recursive build of marshmallow schema
- from marshmallow_mongoengine.convert import convert_field
- return functools.partial(
- self.MARSHMALLOW_FIELD_CLS,
- convert_field(self.mongoengine_field.field)
- )
- def get_field_builder_for_data_type(field_me):
- field_me_types = inspect.getmro(type(field_me))
- for field_me_type in field_me_types:
- if field_me_type in FIELD_MAPPING:
- field_ma_cls = FIELD_MAPPING[field_me_type]
- break
- else:
- raise ModelConversionError(
- 'Could not find field of type {0}.'.format(field_me))
- return field_ma_cls(field_me)
- FIELD_MAPPING = {
- }
- def register_field_builder(mongo_field_cls, builder):
- """
- Register a :class MetaFieldBuilder: to a given Mongoengine Field
- :param mongo_field_cls: Mongoengine Field
- :param build: field_builder to register
- """
- FIELD_MAPPING[mongo_field_cls] = builder
- def register_field(mongo_field_cls, marshmallow_field_cls,
- available_params=()):
- """
- Bind a marshmallow field to it corresponding mongoengine field
- :param mongo_field_cls: Mongoengine Field
- :param marshmallow_field_cls: Marshmallow Field
- :param available_params: List of :class marshmallow_mongoengine.cnoversion.params.MetaParam:
- instances to import the mongoengine field config to marshmallow
- """
- class Builder(MetaFieldBuilder):
- AVAILABLE_PARAMS = available_params
- MARSHMALLOW_FIELD_CLS = marshmallow_field_cls
- register_field_builder(mongo_field_cls, Builder)
- register_field(me.fields.BinaryField, ma_fields.Integer,
- available_params=(params.SizeParam,))
- register_field(me.fields.BooleanField, ma_fields.Boolean)
- register_field(me.fields.ComplexDateTimeField, ma_fields.DateTime)
- register_field(me.fields.DateTimeField, ma_fields.DateTime)
- register_field(me.fields.DecimalField, ma_fields.Decimal,
- available_params=(params.SizeParam, params.PrecisionParam))
- register_field(me.fields.DictField, ma_fields.Raw)
- register_field(me.fields.DynamicField, ma_fields.Raw)
- register_field(me.fields.EmailField, ma_fields.Email,
- available_params=(params.LenghtParam,))
- register_field(me.fields.FloatField, ma_fields.Float,
- available_params=(params.SizeParam,))
- register_field(me.fields.GenericEmbeddedDocumentField,
- ma_fields.GenericEmbeddedDocument)
- register_field_builder(me.fields.GenericReferenceField, GenericReferenceBuilder)
- register_field_builder(me.fields.ReferenceField, ReferenceBuilder)
- # FilesField and ImageField can't be simply displayed...
- register_field(me.fields.FileField, ma_fields.Skip)
- register_field(me.fields.ImageField, ma_fields.Skip)
- register_field(me.fields.IntField, ma_fields.Integer,
- available_params=(params.SizeParam,))
- register_field(me.fields.LongField, ma_fields.Integer,
- available_params=(params.SizeParam,))
- register_field(me.fields.ObjectIdField, ma_fields.ObjectId)
- register_field(me.fields.UUIDField, ma_fields.UUID)
- register_field(me.fields.PointField, ma_fields.Point)
- register_field(me.fields.SequenceField, ma_fields.Integer,
- available_params=(params.SizeParam,)) # TODO: handle value_decorator
- register_field(me.fields.StringField, ma_fields.String,
- available_params=(params.LenghtParam, params.RegexParam))
- register_field(me.fields.URLField, ma_fields.URL,
- available_params=(params.LenghtParam,))
- register_field_builder(me.fields.EmbeddedDocumentField, EmbeddedDocumentBuilder)
- register_field_builder(me.fields.ListField, ListBuilder)
- register_field_builder(me.fields.MapField, MapBuilder)
- register_field_builder(me.fields.SortedListField, ListBuilder)
- # TODO: finish fields...
- # me.fields.GeoPointField: ma_fields.GeoPoint,
- # me.fields.LineStringField: ma_fields.LineString,
- # me.fields.PolygonField: ma_fields.Polygon,
- # me.fields.MultiPointField: ma_fields.MultiPoint,
- # me.fields.MultiLineStringField: ma_fields.MultiLineString,
- # me.fields.MultiPolygonField: ma_fields.MultiPolygon,
|