如何以编程方式上传本地文件作为 Django 模型字段?

Sta*_*ute 2 python django django-storage

我在尝试从本地路径上传文件时遇到问题FileField

我已在S3存储桶中正确配置了 CDN 后端,并将其用作PrivateMediaStorage我的模型字段之一:

class MyModel(models.Model):
    some_file = models.FileField(storage=PrivateMediaStorage())
    ...
Run Code Online (Sandbox Code Playgroud)

通过这个非常简单的配置,每当我创建/更新模型时,django-admin它都会被保存并附加文件,并some_file正确上传到S3存储桶。

然而,如果我尝试以编程方式创建/更新模型实例(例如通过自定义manage.py命令),则会创建模型实例本身,但附件永远不会上传到 CDN。这是我用来上传文件的代码的简化版本:

class Command(BaseCommand):
    help = 'Creates dummy instance for quicker configuration'

    def handle(self, *args, **options):
        some_file = os.path.join(os.path.dirname(__file__), '../../../temporary/some_image.png')

        if not os.path.exists(some_file):
            raise CommandError(f'File {some_file} does not exist')
        else: 
            instance, created = MyModel.objects.get_or_create(defaults={'some_file': some_file}, ...)
Run Code Online (Sandbox Code Playgroud)

我的实现中缺少什么以及需要调整什么才能允许从本地存储上传文件?

dir*_*ten 7

您将一个字符串( 的结果os.path.join())传递到您的some_file字段,但您需要向它传递一个实际File对象。

直接在模型上保存文件的最简单方法是使用 的FieldFile方法save()

作为所提供案例的可行解决方案,创建记录的有效方法是:

instance = MyModel.objects.create(some_file=File(file=open(some_file, 'rb'), name='some_name.png'))
Run Code Online (Sandbox Code Playgroud)

或者甚至更好地用于pathlib动态获取名称:

from pathlib import Path

instance = MyModel.objects.create(some_file=File(file=open(some_file, 'rb'), name=Path(some_file).name))
Run Code Online (Sandbox Code Playgroud)

请注意,基于文件获取行不太可能起作用,据我所知,每次打开文件时,使用get_or_create()实例File作为参数执行 a 可能每次都会创建一个新行。最好将文件字段放入defaults

with open(some_file, 'rb') as file:
    instance, created = MyModel.objects.get_or_create(
        some_other_field=..., 
        defaults={'some_file': File(
            file=file, 
            name=pathlib.Path(some_file).name
            )}
    )
Run Code Online (Sandbox Code Playgroud)