当前位置: 首页 > 工具软件 > EvalAI > 使用案例 >

【从EvalAI学习Django】自定义文件上传保存地址 | FileField | upload_to | deconstructible

郭俊拔
2023-12-01

需求

文件上传后如果不设置会使用默认名称进行保存,重复存储后会在原名称后添加_1、_2等避免重名,那么如何将文件存储名称和其在数据库的保存数据联系上是一个问题。

分析

文件字段为FileField,其特有的参数为upload_to和storage,upload_to决定文件保存地址,比如当upload_to="uploads/"时,实际地址为settings.MEDIA_ROOT/uploadsMEDIA_ROOT可以在配置文件中设置;storage为存储操作,默认的为FileStorageSystem

其中upload_to还有一种定义方式就是函数返回式定义,其原理为:FileField字段对象中存在generate_filename方法,其中就对upload_to的类型进行判断,如果是callable(self.upload_to),则传入相关参数调用重新赋值,而在FieldFile中的save方法会调用FileField中的generate_filename方法生成给storage所保存的文件地址。

FieldFileFileField是两个东西。

如官方文档中的例子:

# 定义函数user_directory_path接收两个参数。
def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)

文档的例子有个问题就是模型在迁移的时候会对字段进行序列化转为migrations文件,但是这里所使用的函数返回式的定义也会被添加到迁移文件中,所以要使用deconstructible来避免这个问题,因为一旦被加入迁移文件,而迁移文件存在迭代更新的逻辑,之后更改文件存储方式则极其困难。

EvalAI实例

EvalAI中存在大量的文件字段,当文件多了起来,要对文件名称进行一定的规范才更容易管理,比如将比赛的pk作为文件名的一部分,这就让文件名更加的合理以及规范。

# 添加装饰器deconstructible避免加入迁移文件的问题
@deconstructible
class RandomFileName(object):
    def __init__(self, path):
        self.path = path

    def __call__(self, instance, filename):
    	# split ext 分离出文件后缀名
        extension = os.path.splitext(filename)[1]
        path = self.path
        # 如果传入的地址中存在id字符,则加入实例保存后的pk
        if "id" in self.path and instance.pk:
            path = self.path.format(id=instance.pk)
        # 随机生成文件地址
        filename = "{}{}".format(uuid.uuid4(), extension)
        filename = os.path.join(path, filename)
        return filename
# Model
# 模型文件中添加RandomFileName作为覆盖
... = models.FileField(upload_to=RandomFileName("..."), ...)

参考

 类似资料: