Django 存储 S3 - 使用 ModelSerializer 仅保存文件路径而不使用文件

Sai*_*der 5 django django-models django-file-upload django-serializer django-rest-framework

我正在使用 boto3 将文件上传到 S3 并将其路径保存在 FileField 中。

class SomeFile(models.Model):
    file = models.FileField(upload_to='some_folder', max_length=400, blank=True, null=True)
Run Code Online (Sandbox Code Playgroud)

对于上述模型,以下代码可用于创建记录。

ff = SomeFile(file='file path in S3')
ff.full_clean()
ff.save()
Run Code Online (Sandbox Code Playgroud)

现在,当我使用 ModelSerializer 执行相同操作时。

class SomeFileSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeFile
        fields = ('file')
Run Code Online (Sandbox Code Playgroud)

运行下面的代码后出现此错误

rest_framework.exceptions.ValidationError: {'file': [ErrorDetail(string='提交的数据不是文件。请检查表单上的编码类型。', code='invalid')]}

serializer = SomeFileSerializer(data={'file': 'file path to S3'})
serializer.is_valid(raise_exception=True)
Run Code Online (Sandbox Code Playgroud)

我需要帮助来设置序列化器以接受文件路径,而无需实际拥有该文件。

Kaz*_*iyo 2

我确实遇到了同样的情况,在网上很难找到解决方案。

我们有两种选择来解决这个问题。

1.直接向save方法传递数据

  • 读取操作:使用序列化器的只读 ImageField
  • 写入操作:将 kwargs 传递给 save 方法

序列化器.py

from rest_framework import serializers

class SomeFileSerializer(serializers.ModelSerializer):
    file = serializers.ImageField(read_only=True)

    class Meta:
        model = SomeFile
        fields = ('file')
Run Code Online (Sandbox Code Playgroud)

视图.py

serializer = SomeFileSerializer(data={'file': 'file path to S3'})
serializer.is_valid(raise_exception=True)

# for put method
serializer.save(file=request.data.get('file'))

# for patch method (if partial=True in serializer)
if request.data.get('file'):
    serializer.save(file=request.data.get('file'))
else:
    serializer.save()
Run Code Online (Sandbox Code Playgroud)

2.使用CharField代替ImageField

  • 读取操作:重写to_representation函数以响应绝对url
  • 写入动作:使用CharField来避免ImageField的验证和动作

序列化器.py

from rest_framework import serializers

class SomeFileSerializer(serializers.ModelSerializer):
    file = serializers.CharField(max_length=400)

    class Meta:
        model = SomeFile
        fields = ('file')

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        if instance.file:
            # update filename to response absolute url
            representation['file'] = instance.file_absolute_url
        return representation

Run Code Online (Sandbox Code Playgroud)

模型.py

class SomeFile(models.Model):
    file = models.FileField(upload_to='some_folder', max_length=400, blank=True, null=True)

    @property
    def file_absolute_url(self):
        return self.file.url if self.file else None
Run Code Online (Sandbox Code Playgroud)

虽然我因为 drf_spectaulous 的文档而选择了第二个解决方案,但第一个解决方案很容易实现。