Source code for eve_mongoengine.schema

"""
    eve_mongoengine.schema
    ~~~~~~~~~~~~~~~~~~~~~~

    Mapping mongoengine field types to cerberus schema.

    :copyright: (c) 2014 by Stanislav Heller.
    :license: BSD, see LICENSE for more details.
"""

import copy

from mongoengine import (StringField, IntField, FloatField, BooleanField,
                         DateTimeField, ComplexDateTimeField, URLField,
                         EmailField, LongField, DecimalField, ListField,
                         EmbeddedDocumentField, SortedListField, DictField,
                         MapField, UUIDField, ObjectIdField, LineStringField,
                         GeoPointField, PointField, PolygonField, BinaryField,
                         ReferenceField, DynamicField, FileField)

from eve.exceptions import SchemaException


class SchemaMapper(object):
[docs] """ Default mapper from mongoengine model classes into cerberus dict-like schema. """ _mongoengine_to_cerberus = { StringField: 'string', IntField: 'integer', FloatField: 'float', BooleanField: 'boolean', DateTimeField: 'datetime', ComplexDateTimeField: 'datetime', URLField: 'string', EmailField: 'string', LongField: 'integer', DecimalField: 'float', EmbeddedDocumentField: 'dict', ListField: 'list', SortedListField: 'list', DictField: 'dict', MapField: 'dict', UUIDField: 'string', ObjectIdField: 'objectid', LineStringField: 'dict', GeoPointField: 'list', PointField: 'dict', PolygonField: 'dict', BinaryField: 'string', ReferenceField: 'objectid', FileField: 'media' # NOT SUPPORTED: # ImageField, SequenceField # GenericEmbeddedDocumentField } @classmethod def _resolve_field_class(cls, field): """ Resolves field classes, which are non-standard (derived from existing ones) to get most of it's functionality. If no appropriate class is found for this field, returns DynamicField. """ for klass in field.__class__.mro(): if klass in cls._mongoengine_to_cerberus: return klass return DynamicField @classmethod def create_schema(cls, model_cls, lowercase=True):
[docs] """ :param model_cls: Mongoengine model class, subclass of :class:`mongoengine.Document`. :param lowercase: True if names of resource for model class has to be treated as lowercase string of classname. """ schema = {} for field in model_cls._fields.values(): if field.primary_key: # defined custom primary key -> fail, cos eve doesnt support it raise SchemaException("Custom primery key not allowed - eve " "does not support different id fields " "for resources.") fname = field.db_field if getattr(field, 'eve_field', False): # Do not convert auto-added fields 'updated' and 'created'. # This attribute is injected into model in EveMongoengine's # fix_model_class() method. continue if fname in ('_id', 'id'): # default id field, do not insert it into schema continue schema[fname] = cls.process_field(field, lowercase) return schema @classmethod
def process_field(cls, field, lowercase):
[docs] """ Returns Eve field definition from Mongoengine field :param field: Mongoengine field :param lowercase: True if names of resource for model class has to be treated as lowercase string of classname. """ fdict = {} best_matching_cls = cls._resolve_field_class(field) if best_matching_cls in cls._mongoengine_to_cerberus: cerberus_type = cls._mongoengine_to_cerberus[best_matching_cls] fdict['type'] = cerberus_type if isinstance(field, EmbeddedDocumentField): fdict['schema'] = cls.create_schema(field.document_type) if isinstance(field, ListField): fdict['schema'] = cls.process_field(field.field, lowercase) if field.required: fdict['required'] = True if field.unique: fdict['unique'] = True if field.choices: fdict['allowed'] = field.choices if getattr(field, 'max_length', None) is not None: fdict['maxlength'] = field.max_length if getattr(field, 'min_length', None) is not None: fdict['minlength'] = field.min_length if getattr(field, 'max_value', None) is not None: fdict['max'] = field.max_value if getattr(field, 'min_value', None) is not None: fdict['min'] = field.min_value # special cases if best_matching_cls is ReferenceField: # create data_relation schema resource = field.document_type.__name__ if lowercase: resource = resource.lower() fdict['data_relation'] = { 'resource': resource, 'field': '_id', 'embeddable': True } elif best_matching_cls is DynamicField: fdict['allow_unknown'] = True fdict['type'] = 'dynamic' return fdict @classmethod
def get_subresource_settings(cls, model_cls, resource_name,
[docs] resource_settings, lowercase=True): """ Yields name of subresource domain and it's settings. """ for field in model_cls._fields.values(): if field.__class__ is ReferenceField: fname = field.db_field subresource_settings = copy.deepcopy(resource_settings) subresource = field.document_type.__name__ if lowercase: subresource = subresource.lower() # FIXME what if id is of other type? _url = '%s/<regex("[a-f0-9]{24}"):%s>/%s' subresource_settings['url'] = _url % (subresource, fname, resource_name) yield subresource+resource_name, subresource_settings