如何使 rest_framework 序列化器禁止多余的字段?

Posted

技术标签:

【中文标题】如何使 rest_framework 序列化器禁止多余的字段?【英文标题】:How to make a rest_framework Serializer disallow superfluous fields? 【发布时间】:2014-04-16 16:08:24 【问题描述】:

我注意到序列化器在拒绝未知字段的输入时并不是很严格:

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 在这种情况下出现意外的验证错误?

【问题讨论】:

【参考方案1】:

这绝对有效:

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 ticket 用于此)。

在这种情况下,适用于所有两种情况的稍微不太干净的解决方案是:

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)

【讨论】:

专业提示:set(some_dict)set(some_dict.keys()) 一样好用【参考方案2】:

使用 django REST framework v. 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)

【讨论】:

【参考方案3】:

s.data 不包含 bar 那么重要的用例是什么?

查看文档后,我没有看到原生解决方案。您可以覆盖.validate() 进行检查并以这种方式提高ValidationErrors。我没有用 when 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

【讨论】:

我喜欢这种方法,但 validaterestore_fields 之后被调用,所以它看不到额外的输入条目,我需要在其他地方插入。我的用例是一个常规的 REST API - 我认为如果存在意外的 POST 参数,它应该会引发错误,以便用户在例如输入错误的可选参数。 你说得对,我很抱歉。我通过在.validate() 方法中添加attrs['bar'] = 'blah' 进行测试。 另一种可能的解决方案,虽然我不认为它是干净的。您可以在.validate() 中访问self.context['request']._request.POST。您必须过滤掉某些键,例如 csrfmiddleware_method,但您会得到用户实际提供的内容,并且可以将其与 self.fields.keys() 进行比较。

以上是关于如何使 rest_framework 序列化器禁止多余的字段?的主要内容,如果未能解决你的问题,请参考以下文章

python Django Rest_Framework框架 模型类序列化器(ModelSerializer)详解(图文并茂版)

python Django Rest_Framework框架 模型类序列化器(ModelSerializer)详解(图文并茂版)

python Django Rest_Framework框架 APIView介绍与序列化器详解(图文并茂版)

python Django Rest_Framework框架 APIView介绍与序列化器详解(图文并茂版)

rest_framework

Django.rest_framework:如何序列化一对多?