原来没那么难。
但下面的代码没有经过太多测试
,而且我必须警告你,不检查就不要复制粘贴!
我创造了一个习惯
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
(A)级
File
子类由提供
django-storages
)
这个
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)
,它会自动创建
S3boto3存储文件
取决于电流的内容
file
实例(有时是一个文件,有时是一个简单的字符串,这是filedescriptor业务的一部分)。
现在,动态部分非常简单。实际上,在声明文件字段时,可以向
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
,其中包含一个密钥,该密钥允许我检索包含s3凭据的对象,使用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()
等一下!