Django Rest Framework "This field is required" 仅在 POSTing JSON 时,而不是在 POSTing 表单内容时

Django Rest Framework "This field is required" only when POSTing JSON, not when POSTing form content

我得到一个奇怪的结果 POSTing JSON 到 DRF 端点 returns:

{"photos":["This field is required."],"tags":["This field is required."]}'

而当 POST 输入表单数据时,DRF 并不介意字段为空。

我的模型是:

class Story(CommonInfo):
    user = models.ForeignKey(User)
    text = models.TextField(max_length=5000,blank=True)
    feature = models.ForeignKey("Feature", blank=True, null=True)
    tags = models.ManyToManyField("Tag")

我的序列化程序是:

class StorySerializer(serializers.HyperlinkedModelSerializer):
    user = serializers.CharField(read_only=True) 

    def get_fields(self, *args, **kwargs):
        user = self.context['request'].user
        fields = super(StorySerializer, self).get_fields(*args, **kwargs)
        fields['feature'].queryset = fields['feature'].queryset.filter(user=user)
        fields['photos'].child_relation.queryset = fields['photos'].child_relation.queryset.filter(user=user)
        return fields

    class Meta:
        model = Story
        fields = ('url', 'user', 'text', 'photos', 'feature', 'tags')

我的 api.py 是:

class StoryViewSet(viewsets.ModelViewSet):
    serializer_class = StorySerializer

    def get_queryset(self):
        return self.request.user.story_set.all()

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

结果:

# JSON request doesn't work
IN: requests.post("http://localhost:8001/api/stories/",
               auth=("user", "password",),
               data=json.dumps({'text': 'NEW ONE!'}),
               headers={'Content-type': 'application/json'}
              ).content
OUT: '{"photos":["This field is required."],"tags":["This field is required."]}'

# Form data request does work
IN: requests.post("http://localhost:8001/api/stories/",
               auth=("user", "password",),
               data={'text': 'NEW ONE!'},
              ).content
OUT: '{"url":"http://localhost:8001/api/stories/277/","user":"user","text":"NEW ONE!","photos":[],"feature":null,"tags":[]}'

这里的问题起初并不明显,但它与表单数据的缺陷以及部分数据的处理方式有关。

表单数据有两种 Django REST 框架必须处理的特殊情况

  1. 某些输入没有 "null" 或 "empty" 数据的概念,包括复选框和其他允许多项选择的输入。
  2. 没有任何输入类型支持单个字段的多个值,复选框是一个例外。

这两者结合在一起使得很难在 Django REST 框架中处理接受表单数据,因此它必须处理一些与大多数解析器不同的事情。

  1. 如果没有传入某个字段,则假定为None或该字段的默认值。这是因为没有值的输入不会在表单数据中传递,因此它们的键丢失。
  2. 如果为多值字段传入单个值,它将被视为一个选定值。这是因为从多个复选框中选择的单个复选框与表单数据中的单个复选框完全没有区别。它们都作为一个键传入。

但这同样不适用于JSON。因为您没有为 photostags 键传递一个空列表,所以 DRF 不知道为它提供什么作为默认值,也不会将它传递给序列化程序。因此,序列化程序发现没有传入任何内容并触发验证错误,因为未提供必填字段。

因此解决方案是在使用 JSON 时始终提供所有密钥(不包括 PATCH 请求,这可能是部分请求),即使它们不包含任何数据。