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

问题描述 投票:0回答:2

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

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

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

ff = SomeFile(file='file path in S3')
ff.full_clean()
ff.save()

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

class SomeFileSerializer(serializers.ModelSerializer):
    class Meta:
        model = SomeFile
        fields = ('file')

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

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

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

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

django django-models django-rest-framework django-serializer django-file-upload
2个回答
2
投票

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

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

1.直接将数据传递给保存方法

  • 读取操作:使用序列化器的只读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')

views.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()

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

模型.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

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


0
投票

要将通过 Django FileField 上传的文件存储在 Amazon S3 上而不是本地,您可以使用 django-storages 库以及 Django 的 FileField。以下是有关如何实现此目标的分步指南:

  1. 安装 django-storages:首先,您需要安装 django-storages 库。您可以通过 pip 来完成此操作:

    pip install django-storages

  2. 配置您的 S3 凭证:在 Django 项目的设置中,配置您的 S3 凭证和存储桶名称。例如:

    #settings.py     
    AWS_ACCESS_KEY_ID='your-access-key'    
    AWS_SECRET_ACCESS_KEY = 'your-secret-key'
    AWS_STORAGE_BUCKET_NAME = 'your-bucket-name'
  1. 配置静态和媒体 URL 以指向 S3:修改您的设置以将 S3 用于静态和媒体文件。对于媒体文件,您通常使用 MEDIA_URLDEFAULT_FILE_STORAGE:
    # settings.py
    
    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    MEDIA_URL = 'https://s3.amazonaws.com/%s/' % AWS_STORAGE_BUCKET_NAME
  1. 使用 FileField 更新模型:使用 FileField 定义模型,就像通常在 Django 中一样:
    # models.py
    
    from django.db import models
    
    class MyModel(models.Model):
         upload = models.FileField(upload_to='uploads/')
  1. 运行迁移:进行这些更改后,运行 Django 迁移以将更改应用到您的数据库架构:
    $ python manage.py makemigrations
    $ python manage.py migrate

现在,当您通过 Django 应用程序中的 FileField 上传文件时,它将直接存储在 Amazon S3 上,而不是本地存储在您的服务器上。

© www.soinside.com 2019 - 2024. All rights reserved.