【问题标题】:How to dynamically select storage option for models.FileField?如何为models.FileField动态选择存储选项?
【发布时间】:2016-05-05 20:40:04
【问题描述】:

根据文件扩展名,我希望将文件存储在特定的 AWS 存储桶中。我尝试将函数传递给 storage 选项,类似于动态定义 upload_to 的方式。

但是,这并没有给出预期的结果。在我的模板中,当我尝试使用 href 到 document.docfile.url 时,链接不起作用。

检查外壳,发生这种情况

Document.objects.all()[0].docfile.storage.bucket
 <Bucket: <function aws_bucket at 0x110672050>>

Document.objects.all()[0].docfile.storage.bucket_name
 <function myproject.myapp.models.aws_bucket>

期望的行为是

Document.objects.all()[0].docfile.storage.bucket_name
 'asynch-uploader-txt'
Document.objects.all()[0].docfile.storage.bucket     
 <Bucket: asynch-uploader-txt>

这是我的models.py 文件:

# -*- coding: utf-8 -*-
from django.db import models
from storages.backends.s3boto import S3BotoStorage

def upload_file_to(instance, filename):
    import os
    from django.utils.timezone import now
    filename_base, filename_ext = os.path.splitext(filename)
    return 'files/%s_%s%s' % (
        filename_base,
        now().strftime("%Y%m%d%H%M%S"),
        filename_ext.lower(),
    )

def aws_bucket(instance, filename):
    import os
    filename_base, filename_ext = os.path.splitext(filename)
    return 'asynch-uploader-%s' %(filename_ext[1:])

class Document(models.Model):
    docfile = models.FileField(upload_to=upload_file_to,storage=S3BotoStorage(bucket=aws_bucket))

为什么aws_bucket 是作为函数而不是字符串传递的,就像upload_file_to 那样?我该如何纠正?

【问题讨论】:

    标签: python django boto django-storage


    【解决方案1】:

    对于您想要做的事情,您最好制作一个自定义存储后端并覆盖 S3BotoStorage 的各个部分。 特别是如果您将bucket_name 设为属性,您应该能够获得您想要的行为。

    编辑:

    为了进一步扩展,S3BotoStorage.__init__sourcebucket 作为可选参数。此外,bucket 在类中使用时为@param,使其易于覆盖。以下代码未经测试,但应该足以为您提供一个起点

    class MyS3BotoStorage(S3BotoStorage):
        @property
        def bucket(self):
            if self._filename.endswith('.jpg'):
                return self._get_or_create_bucket('SomeBucketName')
            else:
                return self._get_or_create_bucket('SomeSaneDefaultBucket')
    
        def _save(self, name, content):
            # This part might need some work to normalize the name and all...
            self._filename = name
            return super(MyS3BotoStorage, self)._save(name, content)
    

    【讨论】:

    • 我不确定这会如何工作。我需要在运行时由文件名的扩展名决定存储桶。如果我创建一个继承自 S3BotoStorage 的新类,我是否不需要选择一个固定的存储桶,或者遇到同样的问题?
    • 我做了this,但存在同样的问题,即 docfile.bucket 被分配了一个函数而不是字符串值,并且稍后访问 docfile.url 会出现错误,“无法连接 str 和函数"。
    • 从我所看到的源代码来看,不,你不必在初始化时选择它。如果您覆盖存储桶属性,则您可以访问所有实例变量。我认为您将可以访问该文件名,这将允许您正确计算要使用的存储桶。无论哪种方式都可以查看 S3BotoStorage 源代码。您不必重写太多方法。
    • 我还查看了您发布的 pastebin 链接。您只在那里覆盖 init 方法,我认为您甚至不需要覆盖它。再次使用计算机后,我将再次查看源代码并发布经过编辑的答案
    • 我想我可以看出代码sn-p的意图是什么,但我还是有些疑惑。在存储桶方法覆盖中,我们如何访问 self._filename?当我使用它,然后尝试访问 docile.url 时,我得到的错误是该行上的'MyS3BotoStorage' object has no attribute '_filename'。这是否意味着_save 尚未运行?如何确保它在调用 url 之前运行?
    猜你喜欢
    • 2012-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 2014-03-12
    • 2011-05-25
    相关资源
    最近更新 更多