DRF:允许 GET 请求中的所有字段,但将 POST 限制为一个字段

Jad*_*eda 4 django django-rest-framework

让我们通过例子来理解它。

比如说,我想创建 FileUploader API,它将在数据库中存储 id、file_path、file_name、size、owner 等字段。请参阅下面的示例模型:

class FileUploader(models.Model):
    file = models.FileField()
    name = models.CharField(max_length=100) #name is filename without extension
    version = models.IntegerField(default=0)
    upload_date = models.DateTimeField(auto_now=True, db_index=True)
    owner = models.ForeignKey('auth.User', related_name='uploaded_files')
    size = models.IntegerField(default=0)
Run Code Online (Sandbox Code Playgroud)

现在,对于 API,这就是我想要的:

  1. GET:当我触发 GET 端点时,我希望每个上传的文件都有上述所有字段。

  2. POST:但是对于用户创建/上传文件,她为什么要担心传递所有这些字段。她可以上传文件,然后,我想,序列化程序可以从上传的文件中获取其余字段。

Searilizer: 问题:我创建了下面的序列化程序来达到我的目的。但不确定它是否是实现它的正确方法。

class FileUploaderSerializer(serializers.ModelSerializer):
    #overwrite = serializers.BooleanField()
    class Meta:
        model = FileUploader
        fields = ('file','name','version','upload_date', 'size')
        read_only_fields = ('name','version','owner','upload_date', 'size')

    def create(self, validated_data):
        return FileUploader.objects.create(**validated_data)
Run Code Online (Sandbox Code Playgroud)

供参考的视图集:

class FileUploaderViewSet(viewsets.ModelViewSet):
    serializer_class = FileUploaderSerializer
    parser_classes = (MultiPartParser, FormParser,)

    # overriding default query set
    queryset = LayerFile.objects.all()

    def get_queryset(self, *args, **kwargs):
        qs = super(FileUploaderViewSet, self).get_queryset(*args, **kwargs)
        qs = qs.filter(owner=self.request.user)
        return qs
Run Code Online (Sandbox Code Playgroud)

另外,另一个问题是我希望用户提供名为“覆盖”的额外参数(如果文件已存在于服务器上)。

我不确定如何在序列化程序中访问它。

Jad*_*eda 5

恕我直言,多个序列化程序只会造成越来越多的混乱。

@AaronLelevier - 我按照你的建议研究了其他 stackoverflow 解决方案。但最终决定实施我自己的清洁解决方案。

我更喜欢以下解决方案:

  • 不要更改您的视图集(保留默认值)
  • 在序列化程序中添加 .validate() 方法;以及其他必需的 .create 或 .update() 等。在这里,真正的逻辑将进入 validate() 方法。根据请求类型,我们将根据序列化程序的要求创建validated_data dict。

我认为这是最干净的方法。

示例代码:(修改serializer.py,views.py保持不变)

class LayerFileSerializer(serializers.ModelSerializer):

    class Meta:
        model = LayerFile
        fields = ('id', 'file','name','version','upload_date', 'size', 'maps')
        read_only_fields = ('name','version','owner','upload_date', 'size', 'maps')

    def validate(self, validated_data):
        if self.context['request'].method == 'PATCH':
            # catch here: validated_data only contains filed that are valid for serializer
            # for post/update/patch method only valid field is the file
            # but we need 'name' field as well so trick is to get name from the self.context[request].data
            validated_data['name'] = self.context['request'].data.get('name', None)
            if validated_data['name'] is None or validated_data['name'] == '':
                raise serializers.ValidationError("'name' field cannot be empty!")
            return validated_data
        validated_data['owner'] = self.context['request'].user
        validated_data['name'] = os.path.splitext(validated_data['file'].name)[0]
        validated_data['size'] = validated_data['file'].size
        #print self.context['request'].overwrite
        log.debug("serialized layer data: %s" %validated_data)

        try:
            layer_obj = LayerFile.objects.get(owner=validated_data['owner'], name=validated_data['name'])
        except LayerFile.DoesNotExist:
            layer_obj = None

        if layer_obj:
                raise serializers.ValidationError('Layer with same name already exist. Use overwrite flag to overwrite it.')

        return validated_data

    # This will handle rename
    def partial_update(self, instance, validated_data):
        instance.name = validated_data['name']
        return instance

    # this will handle POST - or layer upload
    def create(self, validated_data):
        return LayerFile.objects.create(**validated_data)
Run Code Online (Sandbox Code Playgroud)