Example #1
0
 def fields_for_model(self, model):
     result = OrderedDict()
     for field in model._meta.sorted_fields:
         ma_field = self.convert_field(field)
         if ma_field:
             result[field.name] = ma_field
     return result
Example #2
0
 def __init__(self, obj=None, extra=None, only=None,
             exclude=None, prefix='', strict=False, many=False,
             context=None):
     if not many and utils.is_collection(obj):
         warnings.warn('Implicit collection handling is deprecated. Set '
                         'many=True to serialize a collection.',
                         category=DeprecationWarning)
     # copy declared fields from metaclass
     self.declared_fields = copy.deepcopy(self._declared_fields)
     self.fields = OrderedDict()
     self.__data = None
     self.obj = obj
     self.many = many
     self.opts = self.OPTIONS_CLASS(self.Meta)
     self.only = only or ()
     self.exclude = exclude or ()
     self.prefix = prefix
     self.strict = strict or self.opts.strict
     #: Callable marshalling object
     self.marshal = fields.Marshaller(prefix=self.prefix, strict=self.strict)
     self.extra = extra
     self.context = context
     if isinstance(obj, types.GeneratorType):
         self.obj = list(obj)
     else:
         self.obj = obj
     self._update_fields(obj)
     # If object is passed in, marshal it immediately so that errors are stored
     if self.obj is not None:
         raw_data = self.marshal(self.obj, self.fields, many=self.many)
         if self.extra:
             raw_data.update(self.extra)
         self.__data = self.process_data(raw_data)
Example #3
0
    def marshal(self, data, fields_dict, many=False):
        """Takes raw data (a dict, list, or other object) and a dict of
        fields to output and filters the data based on those fields.

        :param data: The actual object(s) from which the fields are taken from
        :param dict fields: A dict whose keys will make up the final serialized
                       response output.
        :param bool many: Set to ``True`` if ``data`` is a collection object
                        that is iterable.
        :returns: An OrderedDict of the marshalled data
        """
        if many and data is not None:
            return [self.marshal(d, fields_dict, many=False) for d in data]
        items = []
        for attr_name, field_obj in iteritems(fields_dict):
            key = self.prefix + attr_name
            try:
                item = (key, field_obj.output(attr_name, data))
            except MarshallingError as err:  # Store errors
                if self.strict:
                    raise err
                self.errors[key] = text_type(err)
                item = (key, None)
            except TypeError:
                # field declared as a class, not an instance
                if isinstance(field_obj, type) and \
                    issubclass(field_obj, FieldABC):
                    msg = ('Field for "{0}" must be declared as a '
                           "Field instance, not a class. "
                           'Did you mean "fields.{1}()"?'.format(
                               attr_name, field_obj.__name__))
                    raise TypeError(msg)
                raise
            items.append(item)
        return OrderedDict(items)
Example #4
0
 def __get_fields_to_marshal(self, all_fields):
     '''Filter all_fields based on self.only and self.exclude.'''
     # Default 'only' to all the nested fields
     ret = OrderedDict()
     if all_fields is None:
         return ret
     elif isinstance(self.only, basestring):
         ret[self.only] = all_fields[self.only]
         return ret
     else:
         only = set(all_fields) if self.only is None else set(self.only)
     if self.exclude and self.only:
         # Make sure that only takes precedence
         exclude = set(self.exclude) - only
     else:
         exclude = set([]) if self.exclude is None else set(self.exclude)
     filtered = ((k, v) for k, v in all_fields.items()
                 if k in only and k not in exclude)
     return OrderedDict(filtered)
Example #5
0
 def __init__(self,
              obj=None,
              extra=None,
              only=None,
              exclude=None,
              prefix='',
              strict=False,
              many=False,
              context=None):
     if not many and utils.is_collection(obj):
         warnings.warn(
             'Implicit collection handling is deprecated. Set '
             'many=True to serialize a collection.',
             category=DeprecationWarning)
     # copy declared fields from metaclass
     self.declared_fields = copy.deepcopy(self._declared_fields)
     self.fields = OrderedDict()
     self.__data = None
     self.obj = obj
     self.many = many
     self.opts = self.OPTIONS_CLASS(self.Meta)
     self.only = only or ()
     self.exclude = exclude or ()
     self.prefix = prefix
     self.strict = strict or self.opts.strict
     #: Callable marshalling object
     self.marshal = fields.Marshaller(prefix=self.prefix,
                                      strict=self.strict)
     self.extra = extra
     self.context = context
     if isinstance(obj, types.GeneratorType):
         self.obj = list(obj)
     else:
         self.obj = obj
     self._update_fields(obj)
     # If object is passed in, marshal it immediately so that errors are stored
     if self.obj is not None:
         raw_data = self.marshal(self.obj, self.fields, many=self.many)
         if self.extra:
             raw_data.update(self.extra)
         self.__data = self.process_data(raw_data)
Example #6
0
    def __get_fields(self):
        '''Return the declared fields for the object as an OrderedDict.'''
        ret = OrderedDict()
        declared_fields = copy.deepcopy(self._declared_fields)  # Copy _declared_fields
                                                                # from metaclass
        # Explicitly declared fields
        for field_name, field_obj in iteritems(declared_fields):
            ret[field_name] = field_obj

        # If "fields" option is specified, use those fields
        if self.opts.fields:
            ret = self.__get_opts_fields(ret)

        # if only __init__ param is specified, only return those fields
        if self.only:
            filtered = OrderedDict()
            for field_name in self.only:
                if field_name not in ret:
                    raise AttributeError(
                        '"{0}" is not a valid field for {1}.'
                            .format(field_name, self.obj))
                filtered[field_name] = ret[field_name]
            self.__set_parents(filtered)
            return filtered

        # If "exclude" option or param is specified, remove those fields
        if not isinstance(self.opts.exclude, (list, tuple)) or \
                            not isinstance(self.exclude, (list, tuple)):
            raise ValueError("`exclude` must be a list or tuple.")
        excludes = set(self.opts.exclude + self.exclude)
        if excludes:
            for field_name in excludes:
                ret.pop(field_name, None)
        # Set parents
        self.__set_parents(ret)
        return ret
Example #7
0
    def get_declared_fields(mcs, bases, attrs, field_class):
        """Return the declared fields of a class as an OrderedDict.

        :param tuple bases: Tuple of classes the class is subclassing.
        :param dict attrs: Dictionary of class attributes.
        :param type field_class: The base field class. Any class attribute that
            is of this type will be be returned
        """
        declared = [(field_name, attrs.pop(field_name))
                    for field_name, val in list(iteritems(attrs))
                    if utils.is_instance_or_subclass(val, field_class)]
        # If subclassing another Serializer, inherit its fields
        # Loop in reverse to maintain the correct field order
        for base_class in bases[::-1]:
            if hasattr(base_class, '_declared_fields'):
                declared = list(base_class._declared_fields.items()) + declared
        return OrderedDict(declared)
Example #8
0
    def fields_for_model(self, model):
        fields = self.opts.fields
        exclude = self.opts.exclude

        result = OrderedDict()
        for field in model._meta.sorted_fields:
            if fields and field.name not in fields:
                continue
            if exclude and field.name in exclude:
                continue

        for field in [
                f for f in model._meta.sorted_fields
                if not fields or f.name in fields
        ]:
            ma_field = self.convert_field(field)
            if ma_field:
                result[field.name] = ma_field
        return result
Example #9
0
    def __filter_fields(self, field_names):
        """Return only those field_name:field_obj pairs specified by
        ``field_names``.

        :param set field_names: Field names to include in the final
            return dictionary.
        :returns: An OrderedDict of field_name:field_obj pairs.
        """
        # Convert obj to a dict
        obj_marshallable = utils.to_marshallable_type(self.obj,
                                                      field_names=field_names)
        if obj_marshallable and self.many:
            try:  # Homogeneous collection
                obj_prototype = obj_marshallable[0]
            except IndexError:  # Nothing to serialize
                return self.declared_fields
            obj_dict = utils.to_marshallable_type(obj_prototype,
                                                  field_names=field_names)
        else:
            obj_dict = obj_marshallable
        ret = OrderedDict()
        for key in field_names:
            if key in self.declared_fields:
                ret[key] = self.declared_fields[key]
            else:
                if obj_dict:
                    try:
                        attribute_type = type(obj_dict[key])
                    except KeyError:
                        raise AttributeError(
                            '"{0}" is not a valid field for {1}.'.format(
                                key, self.obj))
                    field_obj = self.TYPE_MAPPING.get(attribute_type,
                                                      fields.Field)()
                else:  # Object is None
                    field_obj = fields.Field()
                # map key -> field (default to Raw)
                ret[key] = field_obj
        return ret
Example #10
0
 class Meta:
     include = OrderedDict([('from', fields.Str()),
                            ('in', fields.Str()),
                            ('@at', fields.Str())])
     ordered = True
Example #11
0
    'github_button':
    False,
    'gratipay_user':
    '******',
    'code_font_size':
    '0.85em',
    'warn_bg':
    '#FFC',
    'warn_border':
    '#EEE',
    # Used to populate the useful-links.html template
    'extra_nav_links':
    OrderedDict([
        ('marshmallow-sqlalchemy @ PyPI',
         'http://pypi.python.org/pypi/marshmallow-sqlalchemy'),
        ('marshmallow-sqlalchemy @ GitHub',
         'http://github.com/marshmallow-code/marshmallow-sqlalchemy'),
        ('Issue Tracker',
         'http://github.com/marshmallow-code/marshmallow-sqlalchemy/issues'),
    ])
}

html_sidebars = {
    'index': [
        'about.html',
        'useful-links.html',
        'searchbox.html',
        'donate.html',
    ],
    '**': [
        'about.html', 'useful-links.html', 'localtoc.html', 'relations.html',
        'searchbox.html', 'donate.html'
Example #12
0
 def test_dict_field_serialize_ordereddict(self, user):
     user.various_data = OrderedDict([("foo", "bar"), ("bar", "baz")])
     field = fields.Dict()
     assert field.serialize('various_data', user) == \
         OrderedDict([("foo", "bar"), ("bar", "baz")])
Example #13
0
class BaseSerializer(base.SchemaABC):
    """Base serializer class with which to define custom serializers.

    Example usage:

    .. code-block:: python

        from datetime import datetime
        from marshmallow import Serializer, fields

        class Person(object):
            def __init__(self, name):
                self.name = name
                self.date_born = datetime.now()

        class PersonSerializer(Serializer):
            name = fields.String()
            date_born = fields.DateTime()

        # Or, equivalently
        class PersonSerializer2(Serializer):
            class Meta:
                fields = ("name", "date_born")

        person = Person("Guido van Rossum")
        serialized = PersonSerializer(person)
        serialized.data
        # OrderedDict([('name', u'Guido van Rossum'),
        #                ('date_born', 'Sat, 09 Nov 2013 00:10:29 -0000')])

    :param obj: The object or collection of objects to be serialized.
    :param dict extra: A dict of extra attributes to bind to the serialized result.
    :param tuple only: A list or tuple of fields to serialize. If ``None``, all
        fields will be serialized.
    :param tuple exclude: A list or tuple of fields to exclude from the
        serialized result.
    :param str prefix: Optional prefix that will be prepended to all the
        serialized field names.
    :param bool strict: If ``True``, raise errors if invalid data are passed in
        instead of failing silently and storing the errors.
    :param bool many: Should be set to ``True`` if ``obj`` is a collection
        so that the object will be serialized to a list.
    """
    TYPE_MAPPING = {
        text_type: fields.String,
        binary_type: fields.String,
        dt.datetime: fields.DateTime,
        float: fields.Float,
        bool: fields.Boolean,
        tuple: fields.Raw,
        list: fields.Raw,
        set: fields.Raw,
        int: fields.Integer,
        uuid.UUID: fields.UUID,
        dt.time: fields.Time,
        dt.date: fields.Date,
        dt.timedelta: fields.TimeDelta,
    }

    OPTIONS_CLASS = SerializerOpts

    #: Custom error handler function. May be ``None``.
    _error_callback = None
    #: List of registered post-processing functions.
    #  NOTE: Initially ``None`` so that every subclass references a different
    #  list of functions
    _data_callbacks = None

    class Meta(object):
        """Options object for a Serializer.

        Example usage: ::

            class Meta:
                fields = ("id", "email", "date_created")
                exclude = ("password", "secret_attribute")

        Available options:

        - ``fields``: Tuple or list of fields to include in the serialized result.
        - ``additional``: Tuple or list of fields to include *in addition* to the
            explicitly declared fields. ``additional`` and ``fields`` are
            mutually-exclusive options.
        - ``exclude``: Tuple or list of fields to exclude in the serialized result.
        - ``dateformat``: Date format for all DateTime fields that do not have their
            date format explicitly specified.
        - ``strict``: If ``True``, raise errors during marshalling rather than
            storing them.
        - ``json_module``: JSON module to use. Defaults to the ``json`` module
            in the stdlib.
        """
        pass

    def __init__(self,
                 obj=None,
                 extra=None,
                 only=None,
                 exclude=None,
                 prefix='',
                 strict=False,
                 many=False,
                 context=None):
        if not many and utils.is_collection(obj):
            warnings.warn(
                'Implicit collection handling is deprecated. Set '
                'many=True to serialize a collection.',
                category=DeprecationWarning)
        # copy declared fields from metaclass
        self.declared_fields = copy.deepcopy(self._declared_fields)
        self.fields = OrderedDict()
        self._data = None  # the cached, serialized data
        self.obj = obj
        self.many = many
        self.opts = self.OPTIONS_CLASS(self.Meta)
        self.only = only or ()
        self.exclude = exclude or ()
        self.prefix = prefix
        self.strict = strict or self.opts.strict
        #: Callable marshalling object
        self.marshal = fields.Marshaller(prefix=self.prefix,
                                         strict=self.strict)
        self.extra = extra
        self.context = context

        if isinstance(obj, types.GeneratorType):
            self.obj = list(obj)
        else:
            self.obj = obj
        self._update_fields(obj)
        # If object is passed in, marshal it immediately so that errors are stored
        if self.obj is not None:
            self._update_data()

    def _update_data(self):
        result = self.marshal(self.obj, self.fields, many=self.many)
        if self.extra:
            if self.many:
                for each in result:
                    each.update(self.extra)
            else:
                result.update(self.extra)
        if self.marshal.errors and callable(self._error_callback):
            self._error_callback(self.marshal.errors, self.obj)

        # invoke registered callbacks
        # NOTE: these callbacks will mutate the data
        if self._data_callbacks:
            for callback in self._data_callbacks:
                if callable(callback):
                    result = callback(self, result, self.obj)
        self._data = result

    @classmethod
    def error_handler(cls, func):
        """Decorator that registers an error handler function for the serializer.
        The function receives the serializer instance, a dictionary of errors,
        and the serialized object as arguments.

        Example: ::

            class UserSerializer(Serializer):
                email = fields.Email()

            @UserSerializer.error_handler
            def handle_errors(serializer, errors, obj):
                raise ValueError('An error occurred while marshalling {}'.format(obj))

        .. versionadded:: 0.7.0

        """
        cls._error_callback = func
        return func

    @classmethod
    def data_handler(cls, func):
        """Decorator that registers a post-processing function for the
        serializer. The function receives the serializer instance, the serialized
        data, and the original object as arguments and should return the
        processed data.

        Example: ::

            class UserSerializer(Serializer):
                name = fields.String()

            @UserSerializer.data_handler
            def add_surname(serializer, data, obj):
                data['surname'] = data['name'].split()[1]
                return data

        .. note::

            You can register multiple handler functions for the same serializer.

        .. versionadded:: 0.7.0

        """
        cls._data_callbacks = cls._data_callbacks or []
        cls._data_callbacks.append(func)
        return func

    @classmethod
    def factory(cls, *args, **kwargs):
        """Create a factory function that returns an instance of the serializer.
        Can be used to "freeze" the serializer's arguments.

        Example: ::

            serialize_user = UserSerializer.factory(strict=True)
            user = User(email='invalidemail')
            serialize_user(user)  # => raises MarshallingError

        :param args: Takes the same positional and keyword arguments as the
            serializer's constructor
        :rtype: A ``functools.partial`` object (from the standard library)
        :return: A function that returns instances of the serializer, fixed with
            the passed arguments.

        .. versionadded:: 0.5.5

        """
        factory_func = functools.partial(cls, *args, **kwargs)
        functools.update_wrapper(factory_func, cls)
        return factory_func

    def _update_fields(self, obj):
        """Update fields based on the passed in object."""
        # if only __init__ param is specified, only return those fields
        if self.only:
            ret = self.__filter_fields(self.only)
            self.__set_field_attrs(ret)
            self.fields = ret
            return self.fields

        if self.opts.fields:
            # Return only fields specified in fields option
            field_names = set(self.opts.fields)
        elif self.opts.additional:
            # Return declared fields + additional fields
            field_names = set(self.declared_fields.keys()) | set(
                self.opts.additional)
        else:
            field_names = set(self.declared_fields.keys())

        # If "exclude" option or param is specified, remove those fields
        excludes = set(self.opts.exclude) | set(self.exclude)
        if excludes:
            field_names = field_names - excludes
        ret = self.__filter_fields(field_names)
        # Set parents
        self.__set_field_attrs(ret)
        self.fields = ret
        return self.fields

    def __set_field_attrs(self, fields_dict):
        """Set the parents of all field objects in fields_dict to self, and
        set the dateformat specified in ``class Meta``, if necessary.
        """
        for field_name, field_obj in iteritems(fields_dict):
            if not field_obj.parent:
                field_obj.parent = self
            if not field_obj.name:
                field_obj.name = field_name
            if isinstance(field_obj, fields.DateTime):
                if field_obj.dateformat is None:
                    field_obj.dateformat = self.opts.dateformat
        return fields_dict

    def __filter_fields(self, field_names):
        """Return only those field_name:field_obj pairs specified by
        ``field_names``.

        :param set field_names: Field names to include in the final
            return dictionary.
        :returns: An OrderedDict of field_name:field_obj pairs.
        """
        # Convert obj to a dict
        obj_marshallable = utils.to_marshallable_type(self.obj,
                                                      field_names=field_names)
        if obj_marshallable and self.many:
            try:  # Homogeneous collection
                obj_prototype = obj_marshallable[0]
            except IndexError:  # Nothing to serialize
                return self.declared_fields
            obj_dict = utils.to_marshallable_type(obj_prototype,
                                                  field_names=field_names)
        else:
            obj_dict = obj_marshallable
        ret = OrderedDict()
        for key in field_names:
            if key in self.declared_fields:
                ret[key] = self.declared_fields[key]
            else:
                if obj_dict:
                    try:
                        attribute_type = type(obj_dict[key])
                    except KeyError:
                        raise AttributeError(
                            '"{0}" is not a valid field for {1}.'.format(
                                key, self.obj))
                    field_obj = self.TYPE_MAPPING.get(attribute_type,
                                                      fields.Field)()
                else:  # Object is None
                    field_obj = fields.Field()
                # map key -> field (default to Raw)
                ret[key] = field_obj
        return ret

    @property
    def data(self):
        """The serialized data as an :class:`OrderedDict`.
        """
        if not self._data:  # Cache the data
            self._update_data()
        return self._data

    @property
    def json(self):
        """The data as a JSON string."""
        return self.to_json()

    @property
    def errors(self):
        """Dictionary of errors raised during serialization."""
        return self.marshal.errors

    def to_json(self, *args, **kwargs):
        """Return the JSON representation of the data. Takes the same arguments
        as Python's built-in ``json.dumps``.
        """
        ret = self.opts.json_module.dumps(self.data, *args, **kwargs)
        # On Python 2, json.dumps returns bytestrings
        # On Python 3, json.dumps returns unicode
        # Ensure that a bytestring is returned
        if isinstance(ret, text_type):
            return binary_type(ret.encode('utf-8'))
        return ret

    def is_valid(self, field_names=None):
        """Return ``True`` if all data are valid, ``False`` otherwise.

        :param field_names: List of field names (strings) to validate.
            If ``None``, all fields will be validated.
        """
        if field_names is not None and type(field_names) not in (list, tuple):
            raise ValueError("field_names param must be a list or tuple")
        fields_to_validate = field_names or self.fields.keys()
        for fname in fields_to_validate:
            if fname not in self.fields:
                raise KeyError(
                    '"{0}" is not a valid field name.'.format(fname))
            if fname in self.errors:
                return False
        return True
Example #14
0
class BaseSerializer(base.SerializerABC):
    '''Base serializer class with which to define custom serializers.

    Example usage:

    .. code-block:: python

        from datetime import datetime
        from marshmallow import Serializer, fields

        class Person(object):
            def __init__(self, name):
                self.name = name
                self.date_born = datetime.now()

        class PersonSerializer(Serializer):
            name = fields.String()
            date_born = fields.DateTime()

        # Or, equivalently
        class PersonSerializer2(Serializer):
            class Meta:
                fields = ("name", "date_born")

        person = Person("Guido van Rossum")
        serialized = PersonSerializer(person)
        serialized.data
        # OrderedDict([('name', u'Guido van Rossum'),
        #                ('date_born', 'Sat, 09 Nov 2013 00:10:29 -0000')])

    :param obj: The object or collection of objects to be serialized.
    :param dict extra: A dict of extra attributes to bind to the serialized result.
    :param tuple only: A list or tuple of fields to serialize. If ``None``, all
        fields will be serialized.
    :param tuple exclude: A list or tuple of fields to exclude from the
        serialized result.
    :param str prefix: Optional prefix that will be prepended to all the
        serialized field names.
    :param bool strict: If ``True``, raise errors if invalid data are passed in
        instead of failing silently and storing the errors.
    :param bool many: Should be set to ``True`` if ``obj`` is a collection
        so that the object will be serialized to a list.
    '''
    TYPE_MAPPING = {
        text_type: fields.String,
        binary_type: fields.String,
        dt.datetime: fields.DateTime,
        float: fields.Float,
        bool: fields.Boolean,
        tuple: fields.Raw,
        list: fields.Raw,
        set: fields.Raw,
        int: fields.Integer,
        uuid.UUID: fields.UUID,
        dt.time: fields.Time,
        dt.date: fields.Date,
        dt.timedelta: fields.TimeDelta,
    }

    OPTIONS_CLASS = SerializerOpts

    class Meta(object):
        '''Options object for a Serializer.

        Example usage: ::

            class Meta:
                fields = ("id", "email", "date_created")
                exclude = ("password", "secret_attribute")

        Available options:

        - ``fields``: Tuple or list of fields to include in the serialized result.
        - ``additional``: Tuple or list of fields to include *in addition* to the
            explicitly declared fields. ``additional`` and ``fields`` are
            mutually-exclusive options.
        - ``exclude``: Tuple or list of fields to exclude in the serialized result.
        - ``dateformat``: Date format for all DateTime fields that do not have their
            date format explicitly specified.
        - ``strict``: If ``True``, raise errors during marshalling rather than
            storing them.
        - ``json_module``: JSON module to use. Defaults to the ``json`` module
            in the stdlib.
        '''
        pass

    def __init__(self, obj=None, extra=None, only=None,
                exclude=None, prefix='', strict=False, many=False,
                context=None):
        if not many and utils.is_collection(obj):
            warnings.warn('Implicit collection handling is deprecated. Set '
                            'many=True to serialize a collection.',
                            category=DeprecationWarning)
        # copy declared fields from metaclass
        self.declared_fields = copy.deepcopy(self._declared_fields)
        self.fields = OrderedDict()
        self.__data = None
        self.obj = obj
        self.many = many
        self.opts = self.OPTIONS_CLASS(self.Meta)
        self.only = only or ()
        self.exclude = exclude or ()
        self.prefix = prefix
        self.strict = strict or self.opts.strict
        #: Callable marshalling object
        self.marshal = fields.Marshaller(prefix=self.prefix, strict=self.strict)
        self.extra = extra
        self.context = context
        if isinstance(obj, types.GeneratorType):
            self.obj = list(obj)
        else:
            self.obj = obj
        self._update_fields(obj)
        # If object is passed in, marshal it immediately so that errors are stored
        if self.obj is not None:
            raw_data = self.marshal(self.obj, self.fields, many=self.many)
            if self.extra:
                raw_data.update(self.extra)
            self.__data = self.process_data(raw_data)

    @classmethod
    def factory(cls, *args, **kwargs):
        """Create a factory function that returns an instance of the serializer.
        Can be used to "freeze" the serializer's arguments.

        Example usage: ::

            serialize_user = UserSerializer.factory(strict=True)
            user = User(email='invalidemail')
            serialize_user(user)  # => raises MarshallingError

        :param args: Takes the same positional and keyword arguments as the
            serializer's constructor
        :rtype: A ``functools.partial`` object (from the standard library)
        :return: A function that returns instances of the serializer, fixed with
            the passed arguments.

        .. versionadded:: 0.5.5

        """
        factory_func = functools.partial(cls, *args, **kwargs)
        functools.update_wrapper(factory_func, cls)
        return factory_func

    def _update_fields(self, obj):
        """Update fields based on the passed in object."""
        # if only __init__ param is specified, only return those fields
        if self.only:
            ret = self.__filter_fields(self.only)
            self.__set_field_attrs(ret)
            self.fields = ret
            return self.fields

        if self.opts.fields:
            # Return only fields specified in fields option
            field_names = set(self.opts.fields)
        elif self.opts.additional:
            # Return declared fields + additional fields
            field_names = set(self.declared_fields.keys()) | set(self.opts.additional)
        else:
            field_names = set(self.declared_fields.keys())

        # If "exclude" option or param is specified, remove those fields
        excludes = set(self.opts.exclude) | set(self.exclude)
        if excludes:
            field_names = field_names - excludes
        ret = self.__filter_fields(field_names)
        # Set parents
        self.__set_field_attrs(ret)
        self.fields = ret
        return self.fields

    def __set_field_attrs(self, fields_dict):
        """Set the parents of all field objects in fields_dict to self, and
        set the dateformat specified in ``class Meta``, if necessary.
        """
        for field_name, field_obj in iteritems(fields_dict):
            if not field_obj.parent:
                field_obj.parent = self
            if not field_obj.name:
                field_obj.name = field_name
            if isinstance(field_obj, fields.DateTime):
                if field_obj.dateformat is None:
                    field_obj.dateformat = self.opts.dateformat
        return fields_dict

    def __filter_fields(self, field_names):
        """Return only those field_name:field_obj pairs specified by
        ``field_names``.

        :param set field_names: Field names to include in the final
            return dictionary.
        :returns: An OrderedDict of field_name:field_obj pairs.
        """
        # Convert obj to a dict
        obj_marshallable = utils.to_marshallable_type(self.obj,
            field_names=field_names)
        if obj_marshallable and self.many:
            try:  # Homogeneous collection
                obj_dict = utils.to_marshallable_type(obj_marshallable[0],
                    field_names=field_names)
            except IndexError:  # Nothing to serialize
                return self.declared_fields
        else:
            obj_dict = obj_marshallable
        ret = OrderedDict()
        for key in field_names:
            if key in self.declared_fields:
                ret[key] = self.declared_fields[key]
            else:
                if obj_dict:
                    try:
                        attribute_type = type(obj_dict[key])
                    except KeyError:
                        raise AttributeError(
                            '"{0}" is not a valid field for {1}.'.format(key, self.obj))
                    field_obj = self.TYPE_MAPPING.get(attribute_type, fields.Raw)()
                else:  # Object is None
                    field_obj = fields.Raw()
                # map key -> field (default to Raw)
                ret[key] = field_obj
        return ret

    @property
    def data(self):
        """The serialized data as an :class:`OrderedDict`.
        """
        if not self.__data:  # Cache the data
            raw_data = self.marshal(self.obj, self.fields, many=self.many)
            if self.extra:
                raw_data.update(self.extra)
            self.__data = self.process_data(raw_data)
        return self.__data

    @property
    def json(self):
        """The data as a JSON string."""
        return self.to_json()

    @property
    def errors(self):
        """Dictionary of errors raised during serialization."""
        return self.marshal.errors

    def process_data(self, data):
        """Hook that allows subclasses to modify the final output of the data.

        .. versionadded:: 0.5.5
        """
        return data

    def to_json(self, *args, **kwargs):
        '''Return the JSON representation of the data. Takes the same arguments
        as Pythons built-in ``json.dumps``.
        '''
        ret = self.opts.json_module.dumps(self.data, *args, **kwargs)
        # On Python 2, json.dumps returns bytestrings
        # On Python 3, json.dumps returns unicode
        # Ensure that a bytestring is returned
        if isinstance(ret, text_type):
            return binary_type(ret.encode('utf-8'))
        return ret

    def is_valid(self, field_names=None):
        """Return ``True`` if all data are valid, ``False`` otherwise.

        :param field_names: List of field names (strings) to validate.
            If ``None``, all fields will be validated.
        """
        if field_names is not None and type(field_names) not in (list, tuple):
            raise ValueError("field_names param must be a list or tuple")
        fields_to_validate = field_names or self.fields.keys()
        for fname in fields_to_validate:
            if fname not in self.fields:
                raise KeyError('"{0}" is not a valid field name.'.format(fname))
            if fname in self.errors:
                return False
        return True
Example #15
0
class BaseSerializer(base.SerializerABC):
    '''Base serializer class with which to define custom serializers.

    Example usage:

    .. code-block:: python

        from datetime import datetime
        from marshmallow import Serializer, fields

        class Person(object):
            def __init__(self, name):
                self.name = name
                self.date_born = datetime.now()

        class PersonSerializer(Serializer):
            name = fields.String()
            date_born = fields.DateTime()

        # Or, equivalently
        class PersonSerializer2(Serializer):
            class Meta:
                fields = ("name", "date_born")

        person = Person("Guido van Rossum")
        serialized = PersonSerializer(person)
        serialized.data
        # OrderedDict([('name', u'Guido van Rossum'),
        #                ('date_born', 'Sat, 09 Nov 2013 00:10:29 -0000')])

    :param obj: The object or collection of objects to be serialized.
    :param dict extra: A dict of extra attributes to bind to the serialized result.
    :param tuple only: A list or tuple of fields to serialize. If ``None``, all
        fields will be serialized.
    :param tuple exclude: A list or tuple of fields to exclude from the
        serialized result.
    :param str prefix: Optional prefix that will be prepended to all the
        serialized field names.
    :param bool strict: If ``True``, raise errors if invalid data are passed in
        instead of failing silently and storing the errors.
    :param bool many: Should be set to ``True`` if ``obj`` is a collection
        so that the object will be serialized to a list.
    '''
    TYPE_MAPPING = {
        text_type: fields.String,
        binary_type: fields.String,
        dt.datetime: fields.DateTime,
        float: fields.Float,
        bool: fields.Boolean,
        tuple: fields.Raw,
        list: fields.Raw,
        set: fields.Raw,
        int: fields.Integer,
        uuid.UUID: fields.UUID,
        dt.time: fields.Time,
        dt.date: fields.Date,
        dt.timedelta: fields.TimeDelta,
    }

    class Meta(object):
        '''Options object for a Serializer.

        Example usage: ::

            class Meta:
                fields = ("id", "email", "date_created")
                exclude = ("password", "secret_attribute")

        Available options:

        - ``fields``: Tuple or list of fields to include in the serialized result.
        - ``additional``: Tuple or list of fields to include *in addition* to the
            explicitly declared fields. ``additional`` and ``fields`` are
            mutually-exclusive options.
        - ``exclude``: Tuple or list of fields to exclude in the serialized result.
        - ``dateformat``: Date format for all DateTime fields that do not have their
            date format explicitly specified.
        - ``strict``: If ``True``, raise errors during marshalling rather than
            storing them.
        '''
        pass

    def __init__(self,
                 obj=None,
                 extra=None,
                 only=None,
                 exclude=None,
                 prefix='',
                 strict=False,
                 many=False,
                 context=None):
        if not many and utils.is_collection(obj):
            warnings.warn(
                'Implicit collection handling is deprecated. Set '
                'many=True to serialize a collection.',
                category=DeprecationWarning)
        # copy declared fields from metaclass
        self.declared_fields = copy.deepcopy(self._declared_fields)
        self.fields = OrderedDict()
        self.__data = None
        self.obj = obj
        self.many = many
        self.opts = SerializerOpts(self.Meta)
        self.only = only or ()
        self.exclude = exclude or ()
        self.prefix = prefix
        self.strict = strict or self.opts.strict
        #: Callable marshalling object
        self.marshal = fields.Marshaller(prefix=self.prefix,
                                         strict=self.strict)
        self.extra = extra
        self.context = context
        if isinstance(obj, types.GeneratorType):
            self.obj = list(obj)
        else:
            self.obj = obj
        self._update_fields(obj)
        # If object is passed in, marshal it immediately so that errors are stored
        if self.obj is not None:
            self.__data = self.marshal(self.obj, self.fields, many=self.many)
            if self.extra:
                self.__data.update(self.extra)

    def _update_fields(self, obj):
        '''Update fields based on the passed in object.'''
        # if only __init__ param is specified, only return those fields
        if self.only:
            ret = self.__filter_fields(self.only)
            self.__set_field_attrs(ret)
            self.fields = ret
            return self.fields

        if self.opts.fields:
            # Return only fields specified in fields option
            field_names = set(self.opts.fields)
        elif self.opts.additional:
            # Return declared fields + additional fields
            field_names = set(self.declared_fields.keys()) | set(
                self.opts.additional)
        else:
            field_names = set(self.declared_fields.keys())

        # If "exclude" option or param is specified, remove those fields
        excludes = set(self.opts.exclude) | set(self.exclude)
        if excludes:
            field_names = field_names - excludes
        ret = self.__filter_fields(field_names)
        # Set parents
        self.__set_field_attrs(ret)
        self.fields = ret
        return self.fields

    def __set_field_attrs(self, fields_dict):
        '''Set the parents of all field objects in fields_dict to self, and
        set the dateformat specified in ``class Meta``, if necessary.
        '''
        for field_name, field_obj in iteritems(fields_dict):
            if not field_obj.parent:
                field_obj.parent = self
            if not field_obj.name:
                field_obj.name = field_name
            if isinstance(field_obj, fields.DateTime):
                if field_obj.dateformat is None:
                    field_obj.dateformat = self.opts.dateformat
        return fields_dict

    def __filter_fields(self, field_names):
        '''Return only those field_name:field_obj pairs specified by
        ``field_names``.

        :param set field_names: Field names to include in the final
            return dictionary.
        :returns: An OrderedDict of field_name:field_obj pairs.
        '''
        # Convert obj to a dict
        obj_marshallable = utils.to_marshallable_type(self.obj,
                                                      field_names=field_names)
        if obj_marshallable and self.many:
            try:  # Homogeneous collection
                obj_dict = utils.to_marshallable_type(obj_marshallable[0],
                                                      field_names=field_names)
            except IndexError:  # Nothing to serialize
                return self.declared_fields
        else:
            obj_dict = obj_marshallable
        ret = OrderedDict()
        for key in field_names:
            if key in self.declared_fields:
                ret[key] = self.declared_fields[key]
            else:
                if obj_dict:
                    try:
                        attribute_type = type(obj_dict[key])
                    except KeyError:
                        raise AttributeError(
                            '"{0}" is not a valid field for {1}.'.format(
                                key, self.obj))
                    field_obj = self.TYPE_MAPPING.get(attribute_type,
                                                      fields.Raw)()
                else:  # Object is None
                    field_obj = fields.Raw()
                # map key -> field (default to Raw)
                ret[key] = field_obj
        return ret

    @property
    def data(self):
        '''The serialized data as an ``OrderedDict``.
        '''
        if not self.__data:  # Cache the data
            self.__data = self.marshal(self.obj, self.fields, many=self.many)
            if self.extra:
                self.__data.update(self.extra)
        return self.__data

    @property
    def json(self):
        '''The data as a JSON string.'''
        return self.to_json()

    @property
    def errors(self):
        '''Dictionary of errors raised during serialization.'''
        return self.marshal.errors

    def to_json(self, *args, **kwargs):
        '''Return the JSON representation of the data. Takes the same arguments
        as Pythons built-in ``json.dumps``.
        '''
        ret = json.dumps(self.data, *args, **kwargs)
        # On Python 2, json.dumps returns bytestrings
        # On Python 3, json.dumps returns unicode
        # Ensure that a bytestring is returned
        if isinstance(ret, text_type):
            return binary_type(ret.encode('utf-8'))
        return ret

    def is_valid(self, field_names=None):
        """Return ``True`` if all data are valid, ``False`` otherwise.

        :param field_names: List of field names (strings) to validate.
            If ``None``, all fields will be validated.
        """
        if field_names is not None and type(field_names) not in (list, tuple):
            raise ValueError("field_names param must be a list or tuple")
        fields_to_validate = field_names or self.fields.keys()
        for fname in fields_to_validate:
            if fname not in self.fields:
                raise KeyError(
                    '"{0}" is not a valid field name.'.format(fname))
            if fname in self.errors:
                return False
        return True