如何使rest_frameworkSerializer不允许多余字段?

18 投票
3 回答
3318 浏览
提问于 2025-04-17 21:56

我注意到,序列化器在处理输入时,对于那些未知字段并不是特别严格:

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

有没有办法设置这个Serializer,让它在这种情况下对bar这个意外字段返回一个验证错误呢?

3 个回答

1

s.data里面没有bar,那这种情况有什么用呢?

我查看了文档,没有找到直接的解决办法。你可以重写.validate()这个方法,来进行检查并抛出ValidationErrors。不过我没有在partial=True的情况下测试过,所以如果你在用这个选项,最好自己再确认一下。

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
3

在django REST框架版本3.3.0中,它将是:

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)
14

这个方法肯定是有效的:

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

嵌套和列表序列化器

如果你把这样的序列化器当作另一个序列化器的字段使用,那就不行了。在这种情况下,子序列化器无法访问初始数据,你会遇到一个错误。

使用 ListSerializer(或者 many=True)也是一样,因为列表序列化器的子序列化器无法获取单独的 initial_data 项目(在GitHub上有一个相关问题)。

在这种情况下,有一个稍微不那么优雅但在这两种情况下都有效的解决方案是:

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)

撰写回答