Django Rest Framework只有在POST JSON时才需要"此字段",而不是在POST表单内容时

awi*_*ery 5 django-rest-framework

我得到一个奇怪的结果,即POSTJSON到DRF端点返回:

{"photos":["This field is required."],"tags":["This field is required."]}'
Run Code Online (Sandbox Code Playgroud)

而当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")
Run Code Online (Sandbox Code Playgroud)

我的序列化器是:

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')
Run Code Online (Sandbox Code Playgroud)

而我的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)
Run Code Online (Sandbox Code Playgroud)

结果:

# 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":[]}'
Run Code Online (Sandbox Code Playgroud)

Kev*_*own 5

这里的问题一开始并不明显,但它与表单数据的缺点以及如何处理部分数据有关.

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

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

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

  1. 如果未传入字段,则假定None该字段是字段的默认值.这是因为没有值的输入不会在表单数据中传递,因此缺少其键.
  2. 如果为多值字段传递单个值,则将其视为一个选定值.这是因为在表单数据中从多个复选框和单个复选框中选择的单个复选框之间没有区别.它们都作为单个密钥传入.

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

所以解决方案是在使用JSON时不断提供所有密钥(不包括PATCH可能是部分的请求),即使它们不包含数据也是如此.