gpt4 book ai didi

django - 如何将 Django FileField 与动态 Amazon S3 存储桶一起使用?

转载 作者:行者123 更新时间:2023-12-05 04:05:52 25 4
gpt4 key购买 nike

我有一个带有文件字段的 Django 模型,以及一个使用 Amazon S3 存储桶的默认存储(通过出色的 django-storage )。

我的问题不是将文件上传到动态文件夹路径(正如我们在许多其他答案中看到的那样)。我的问题更深层次和双重:

  • 文件已经在 Amazon S3 存储桶中,我不想下载-重新上传它们(更糟的是:我对它们只有读取权限)。
  • 可通过 S3 凭据访问文件,这些凭据可能因文件而异(也就是说,文件可以位于不同的存储桶中,并通过不同的凭据进行访问)。因此,我的 FileField 必须有动态存储

有什么想法吗?

(Djabgo 1.11,Python 3)。

最佳答案

原来并没有那么难。 但是下面的代码没有经过太多测试,我必须警告你不要在没有检查的情况下复制粘贴!

我已经创建了一个自定义的 FileField 子类:

class DynamicS3BucketFileField(models.FileField):
attr_class = S3Boto3StorageFile
descriptor_class = DynamicS3BucketFileDescriptor

def pre_save(self, model_instance, add):
return getattr(model_instance, self.attname)

请注意,attr_class 专门使用 S3Boto3StorageFile 类(django-storages 提供的 File 子类).

pre_save 重载只有一个目标:避免尝试重新上传文件的内部 file.save 调用。

魔法发生在 FileDescriptor 子类中:

class DynamicS3BucketFileDescriptor(FileDescriptor):
def __get__(self, instance, cls=None):
if instance is None:
return self

# Copied from FileDescriptor
if self.field.name in instance.__dict__:
file = instance.__dict__[self.field.name]
else:
instance.refresh_from_db(fields=[self.field.name])
file = getattr(instance, self.field.name)

# Make sure to transform storage to a Storage instance.
if callable(self.field.storage):
self.field.storage = self.field.storage(instance)

# The file can be a string here (depending on when/how we access the field).
if isinstance(file, six.string_types):
# We instance file following S3Boto3StorageFile constructor.
file = self.field.attr_class(file, 'rb', self.field.storage)
# We follow here the way FileDescriptor work (see 'return' finish line).
instance.__dict__[self.field.name] = file

# Copied from FileDescriptor. The difference here is that these 3
# properties are set systematically without conditions.
file.instance = instance
file.field = self.field
file.storage = self.field.storage
# Added a very handy property to file.
file.url = self.field.storage.url(file.name)

return instance.__dict__[self.field.name]

上面的代码采用了适合我的情况的 FileDescriptor 的一些内部代码。请注意 if callable(self.field.storage):,解释如下。

关键行是:file = self.field.attr_class(file, 'rb', self.field.storage),它会自动创建一个有效的 S3Boto3StorageFile 实例 取决于当前 file 实例的内容(有时,它是一个文件,有时它是一个简单的字符串,这是 FileDescriptor 业务的一部分)。

现在,动态部分非常简单。实际上,当声明一个 FileField 时,您可以向 storage 选项提供一个函数。像这样:

class MyMedia(models.Model):
class Meta:
app_label = 'appname'

mediaset = models.ForeignKey(Mediaset, on_delete=models.CASCADE, related_name='media_files')
file = DynamicS3BucketFileField(null=True, blank=True, storage=get_fits_file_storage)

函数 get_fits_file_storage 将使用一个参数调用:MyMedia 的实例。因此,我可以使用该对象的任何属性来返回有效存储。在我的示例中,mediaset 包含一个 key ,该 key 允许我检索包含 S3 凭据的对象,我可以使用该对象构建一个 S3Boto3Storage 实例(django 提供的另一个类-存储).

具体来说:

def get_fits_file_storage(instance):
name = instance.mediaset.archive_storage_name
return instance.mediaset.archive.bucket_keys.get(name= name).get_storage()

瞧瞧!

关于django - 如何将 Django FileField 与动态 Amazon S3 存储桶一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50621968/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com