Example #1
0
    def get_field_names(self) -> Iterable[str]:
        """
        Returns the list of all field names that should be created when instantiating this serializer class. This is
        based on the default set of fields, but also takes into account the `Meta.fields` or `Meta.exclude` options
        if they have been specified.
        """
        # Retrieve names of the declared fields.
        declared_field_names = self._declared_fields.keys()

        # Read configuration from Meta class.
        meta = getattr(self, 'Meta', None)
        fields = getattr(meta, 'fields', None)
        exclude = getattr(meta, 'exclude', None)

        if fields and fields != rest_framework.serializers.ALL_FIELDS and not isinstance(
                fields, (list, tuple)):
            raise TypeError(
                "The `fields` option must be a list or tuple or '__all__'. Got '{type}'."
                .format(type=type(fields).__name__))

        if exclude and not isinstance(exclude, (list, tuple)):
            raise TypeError(
                "The `exclude` option must be a list or tuple. Got '{type}'.".
                format(type=type(exclude).__name__))

        assert not (fields and exclude), (
            "Cannot set both `fields` and `exclude` options on serializer '{serializer_class}'."
            .format(serializer_class=self.__class__.__name__))

        # If fields is not specified, or is the magic all fields option, make it a list consisting of the all fields
        # placeholder for now.
        if fields is None or fields == rest_framework.serializers.ALL_FIELDS:
            fields = [rest_framework.serializers.ALL_FIELDS]

        # For explicitly specified fields, ensure that they are valid.
        for field_name in fields:
            assert (
                field_name == rest_framework.serializers.ALL_FIELDS
                or  # all fields magic option
                field_name in self.dataclass_definition.fields
                or  # dataclass fields
                field_name in declared_field_names or  # declared fields
                callable(
                    getattr(self.dataclass_definition.dataclass_type,
                            field_name, None))  # methods
            ), ("The field '{field_name}' was included on serializer {serializer_class} in the `fields` option, "
                "but does not match any dataclass field.".format(
                    field_name=field_name,
                    serializer_class=self.__class__.__name__))

        if rest_framework.serializers.ALL_FIELDS not in fields:
            # If there are only explicitly specified fields, ensure that all declared fields are included. Do not
            # require any fields that are declared in a parent class, in order to allow serializer subclasses to only
            # include a subset of fields.
            required_field_names = set(declared_field_names)
            for cls in self.__class__.__bases__:
                required_field_names -= set(
                    getattr(cls, '_declared_fields', []))

            for field_name in required_field_names:
                assert field_name in fields, (
                    "The field '{field_name}' was declared on serializer '{serializer_class}', but has not been "
                    "included in the `fields` option.".format(
                        field_name=field_name,
                        serializer_class=self.__class__.__name__))
            return list(fields)

        # The field list now includes the magic all fields option, so replace it with the default field names.
        fields = list(fields)
        fields.remove(rest_framework.serializers.ALL_FIELDS)
        fields.extend(self.get_default_field_names(declared_field_names))

        if exclude is not None:
            # If `Meta.exclude` is included, then remove those fields.
            for field_name in exclude:
                assert field_name not in declared_field_names, (
                    "Cannot both declare the field '{field_name}' and include it in '{serializer_class}' `exclude` "
                    "option. Remove the field or, if inherited from a parent serializer, disable with "
                    "`{field_name} = None`.".format(
                        field_name=field_name,
                        serializer_class=self.__class__.__name__))

                assert field_name in fields, (
                    "The field '{field_name}' was included on serializer {serializer_class} in the `exclude` option, "
                    "but does not match any dataclass field.".format(
                        field_name=field_name,
                        serializer_class=self.__class__.__name__))

                fields.remove(field_name)

        return fields
Example #2
0
    def get_field_names(self, declared_fields, dataclass_fields):

        fields = getattr(self.Meta, "fields", None)
        exclude = getattr(self.Meta, "exclude", None)

        if fields and fields != serializers.ALL_FIELDS and not isinstance(
                fields, (list, tuple)):
            raise TypeError(
                'The `fields` option must be a list or tuple or "__all__". '
                "Got %s." % type(fields).__name__)

        if exclude and not isinstance(exclude, (list, tuple)):
            raise TypeError(
                "The `exclude` option must be a list or tuple. Got %s." %
                type(exclude).__name__)

        assert not (fields and exclude), (
            "Cannot set both 'fields' and 'exclude' options on "
            "serializer {serializer_class}.".format(
                serializer_class=self.__class__.__name__))

        assert not (fields is None and exclude is None), (
            "Creating a DataclassSerializer without either the 'fields' attribute "
            "is not disallowed. Add an explicit fields = '__all__' to the "
            "{serializer_class} serializer.".format(
                serializer_class=self.__class__.__name__), )

        if fields == serializers.ALL_FIELDS:
            fields = None

        if fields is not None:
            # Ensure that all declared fields have also been included in the
            # `Meta.fields` option.

            # Do not require any fields that are declared in a parent class,
            # in order to allow serializer subclasses to only include
            # a subset of fields.
            required_field_names = set(declared_fields)
            for cls in self.__class__.__bases__:
                required_field_names -= set(
                    getattr(cls, "_declared_fields", []))

            for field_name in required_field_names:
                assert field_name in fields, (
                    "The field '{field_name}' was declared on serializer "
                    "{serializer_class}, but has not been included in the "
                    "'fields' option.".format(
                        field_name=field_name,
                        serializer_class=self.__class__.__name__))
            return fields

        # Use the default set of field names if `Meta.fields` is not specified.
        fields = self.get_default_field_names(declared_fields,
                                              dataclass_fields)

        if exclude is not None:
            # If `Meta.exclude` is included, then remove those fields.
            for field_name in exclude:
                assert field_name not in self._declared_fields, (
                    "Cannot both declare the field '{field_name}' and include "
                    "it in the {serializer_class} 'exclude' option. Remove the "
                    "field or, if inherited from a parent serializer, disable "
                    "with `{field_name} = None`.".format(
                        field_name=field_name,
                        serializer_class=self.__class__.__name__))

                assert field_name in fields, (
                    "The field '{field_name}' was included on serializer "
                    "{serializer_class} in the 'exclude' option, but does "
                    "not match any model field.".format(
                        field_name=field_name,
                        serializer_class=self.__class__.__name__))
                fields.remove(field_name)

        return fields