How to make a rest_framework Serializer disallow superfluous fields?

后端 未结 3 1612
Happy的楠姐
Happy的楠姐 2021-01-11 11:15

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

         


        
相关标签:
3条回答
  • 2021-01-11 11:40

    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)
    
    0 讨论(0)
  • 2021-01-11 11:56

    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
    
    0 讨论(0)
  • 2021-01-11 11:57

    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)
    
    0 讨论(0)
提交回复
热议问题