예제 #1
0
    def from_representation(self, representation):
        """Convert given representation dict into internal object.

        Internal object is simply a dictionary of values with respect to field
        sources.

        This does not check if all required fields exist or values are
        valid in terms of value validation
        (see: :meth:`BaseField.validate()`) but still requires all of passed
        representation values to be well formed representation (success call
        to ``field.from_representation``).

        In case of malformed representation it will run additional validation
        only to provide a full detailed exception about all that might be
        wrong with provided representation.

        Args:
           representation (dict): dictionary with field representation values

        Raises:
            DeserializationError: when at least one representation field
                is not formed as expected by field object. Information
                about additional forbidden/missing/invalid fields is provided
                as well.

        """
        object_dict = {}
        failed = {}

        for name, field in self.fields.items():
            if name not in representation:
                continue

            try:
                # if field has explicitely specified source then use it
                # else fallback to field name.
                # Note: field does not know its name
                object_dict[field.source or name] = field.from_representation(
                    representation[name])
            except ValueError as err:
                failed[name] = str(err)

        if failed:
            # if failed to parse we eagerly perform validation so full
            # information about what is wrong will be returned
            try:
                self.validate(object_dict)
                # note: this exception can be reached with partial==True
                # since do not support partial updates yet this has 'no cover'
                raise DeserializationError()  # pragma: no cover
            except DeserializationError as err:
                err.failed = failed
                raise

        return object_dict
예제 #2
0
    def validate(self, object_dict, partial=False):
        """
        Validate given internal object agains missing/forbidden/invalid
        fields values using fields definitions defined in serializer.

        Args:
            object_dict (dict): internal object dictionart to perform
              to validate
            partial (bool): if set to True then incomplete object_dict
              is accepter and will not raise any exceptions when one
              of fields is missing

        Raises:
            DeserializationError:

        """

        # we are working on object_dict not an representation so there
        # is a need to annotate sources differently
        sources = {
            # TODO: handling of '*' sources here is a bit terryfying
            # TODO: maybe this needs more care in future releases
            field.source or name if field.source != "*" else name: field
            for name, field in self.fields.items()
        }

        # note: we are checking for all mising and invalid fields so we can
        # return exception with all fields that are missing and should
        # exist instead of single one
        missing = [
            name for name, field in sources.items()
            if all((not partial, name not in object_dict, not field.read_only))
        ]

        forbidden = [
            name for name in object_dict
            if any((name not in sources, sources[name].read_only))
        ]

        invalid = {}
        for name, value in object_dict.items():
            try:
                sources[name].validate(value)
            except ValidationError as err:
                invalid[name] = str(err)

        if any([missing, forbidden, invalid]):
            raise DeserializationError(missing, forbidden, invalid)
예제 #3
0
    def validate(self, object_dict, partial=False):
        """Validate given internal object returned by ``to_representation()``.

        Internal object is validated against missing/forbidden/invalid fields
        values using fields definitions defined in serializer.

        Args:
            object_dict (dict): internal object dictionart to perform
              to validate
            partial (bool): if set to True then incomplete object_dict
              is accepter and will not raise any exceptions when one
              of fields is missing

        Raises:
            DeserializationError:

        """
        # we are working on object_dict not an representation so there
        # is a need to annotate sources differently
        sources = {
            _source(name, field): field
            for name, field in self.fields.items()
        }

        # note: we are checking for all mising and invalid fields so we can
        # return exception with all fields that are missing and should
        # exist instead of single one
        missing = [
            name for name, field in sources.items()
            if all((not partial, name not in object_dict, not field.read_only))
        ]

        forbidden = [
            name for name in object_dict
            if any((name not in sources, sources[name].read_only))
        ]

        invalid = {}
        for name, value in object_dict.items():
            try:
                field = sources[name]

                if field.many:
                    for single_value in value:
                        field.validate(single_value)
                else:
                    field.validate(value)

            except ValueError as err:
                invalid[name] = str(err)

        if any([missing, forbidden, invalid]):
            # note: We have validated internal object instance but need to
            #       inform the user about problems with his representation.
            #       This is why we have to do this dirty transformation.
            # note: This will be removed in 1.0.0 where we change how
            #       validation works and where we remove star-like fields.
            # refs: #42 (https://github.com/swistakm/graceful/issues/42)
            sources_to_field_names = {
                _source(name, field): name
                for name, field in self.fields.items()
            }

            def _(names):
                if isinstance(names, list):
                    return [
                        sources_to_field_names.get(name, name)
                        for name in names
                    ]
                elif isinstance(names, dict):
                    return {
                        sources_to_field_names.get(name, name): value
                        for name, value in names.items()
                    }
                else:
                    return names  # pragma: nocover

            raise DeserializationError(_(missing), _(forbidden), _(invalid))
예제 #4
0
    def from_representation(self, representation):
        """Convert given representation dict into internal object.

        Internal object is simply a dictionary of values with respect to field
        sources.

        This does not check if all required fields exist or values are
        valid in terms of value validation
        (see: :meth:`BaseField.validate()`) but still requires all of passed
        representation values to be well formed representation (success call
        to ``field.from_representation``).

        In case of malformed representation it will run additional validation
        only to provide a full detailed exception about all that might be
        wrong with provided representation.

        Args:
           representation (dict): dictionary with field representation values

        Raises:
            DeserializationError: when at least one representation field
                is not formed as expected by field object. Information
                about additional forbidden/missing/invalid fields is provided
                as well.

        """
        object_dict = {}
        failed = {}

        for name, field in self.fields.items():
            if name not in representation:
                continue

            try:
                if (
                        # note: we cannot check for any sequence or iterable
                        #       because of strings and nested dicts.
                        not isinstance(representation[name], (list, tuple))
                        and field.many):
                    raise ValueError("field should be sequence")

                source = _source(name, field)
                value = representation[name]

                if field.many:
                    if not field.allow_null:
                        object_dict[source] = [
                            field.from_representation(single_value)
                            for single_value in value
                        ]
                    else:
                        object_dict[source] = [
                            field.from_representation(single_value)
                            if single_value is not None else None
                            for single_value in value
                        ]
                else:
                    if not field.allow_null:
                        object_dict[source] = field.from_representation(value)
                    else:
                        object_dict[source] = field.from_representation(
                            value) if value else None
            except ValueError as err:
                failed[name] = str(err)

        if failed:
            # if failed to parse we eagerly perform validation so full
            # information about what is wrong will be returned
            try:
                self.validate(object_dict)
                # note: this exception can be reached with partial==True
                # since do not support partial updates yet this has 'no cover'
                raise DeserializationError()  # pragma: no cover
            except DeserializationError as err:
                err.failed = failed
                raise

        return object_dict