How to make a rest_framework Serializer disallow s

2019-04-22 19:09发布

I've noticed that the Serializer isn't really strict when it comes to rejecting input with unknown fields:

In [1]: from rest_framework import serializers

In [2]: class TestSerializer(serializers.Serializer):
   ...:     foo = serializers.CharField()
   ...:     

In [3]: s = TestSerializer(data=dict(foo='foo', bar='bar'))

In [4]: s.is_valid()
Out[4]: True

Is there a way to configure the Serializer to return a validation error about bar being unexpected in this situation?

3条回答
女痞
2楼-- · 2019-04-22 19:53

This definitely works:

class TestSerializer(serializers.Serializer):
    foo = serializers.CharField()

    def validate(self, attrs):
        unknown =  set(self.initial_data) - set(self.fields)
        if unknown:
            raise ValidationError("Unknown field(s): {}".format(", ".join(unknown)))
        return attrs

Nested and list serializers

This will not work if you use a such a serializer as a field in another serializer. In which case, the child serializer won't have access to initial data and you will get an exception.

Same with a ListSerializer (or many=True) as the list serializer's child serializer won't get the individual initial_data items (there is a github ticket for this).

In that case the slightly less clean solution which works all both cases is:

from rest_framework.fields import empty
from rest_framework.settings import api_settings

class TestSerializer(serializers.Serializer):
    foo = serializers.CharField()

    def run_validation(self, data=empty):
        if data is not empty:
            unknown = set(data) - set(self.fields)
            if unknown:
                errors = ["Unknown field: {}".format(f) for f in unknown]
                raise serializers.ValidationError({
                    api_settings.NON_FIELD_ERRORS_KEY: errors,
                })

        return super(TestSerializer, self).run_validation(data)
查看更多
贪生不怕死
3楼-- · 2019-04-22 20:01

s.data does not contain bar so what is the use case where it matters?

After looking at the docs I didn't see a native solution. You could override .validate() to do a check and raise ValidationErrors that way. I didn't test this with when partial=True so you will want to check that if you're using it.

class TestSerializer(serializers.Serializer):
    foo = serializers.CharField()

    def validate(self, attrs):
        has_unknown_fields = set(attrs.keys()) - set(self.fields.keys())

        if has_unknown_fields:
            raise serializers.ValidationError("dont send extra fields")

        return attrs
查看更多
够拽才男人
4楼-- · 2019-04-22 20:04

With django REST framework v. 3.3.0 it will be:

class _FieldSetValidatingSerializer(serializers.Serializer):
  def is_valid(self, raise_exception=False):
    super().is_valid(False)

    fields_keys = set(self.fields.keys())
    input_keys = set(self.initial_data.keys())

    additional_fields = input_keys - fields_keys

    if bool(additional_fields):
      self._errors['fields'] = ['Additional fields not allowed: {}.'.format(list(additional_fields))]

    if self._errors and raise_exception:
      raise ValidationError(self.errors)

    return not bool(self._errors)
查看更多
登录 后发表回答