从模型中删除孤立文件

remove orphaned file from a model

我有以下型号:

class Class(models.Model):
    title = models.CharField(max_length = 60)

    video = models.FileField(
        upload_to = class_files_custom_upload, 
        validators = [
            FileExtensionValidator(['mp4', 'webm', 'mpg', 'mpeg', 'ogv']),
        ]
    )

    section =  models.ForeignKey(Section, on_delete = models.CASCADE)
    created = models.DateTimeField(auto_now_add = True)

    class Meta:
        verbose_name = 'clase'
        verbose_name_plural = 'clases'
        ordering = ['created']

    def __str__(self):
        return self.title

我创建了这个模型的一个实例,但是如果我用任何实例的另一个文件更新 video 字段,之前保存的文件是孤立的,文件占用 space,我想要为了避免它,删除文件。

为此,我自定义文件加载,在 upload_to:

中放置一个可调用对象
def class_files_custom_upload(instance, filename):
    try:
        old_instance = Class.objects.get(id = instance.id)
        old_instance.video.delete()
    except Class.DoesNotExist:
        pass

    return os.path.join('courses/videos', generate_secure_filename(filename))

这样我就达到了我的目的。但是我有几个保存多媒体文件的模型,我必须为每个模型自定义文件加载,实际上做一个几乎等于 class_files_custom_upload 的函数,代码重复,这根本不是最优的。

我试图在 ImageFieldFileField 等各个领域创建一个满足 class_files_custom_upload 函数目标的可重用函数,但我无法做到,因为函数只接收instancefilename2个参数,数据太少无法实现。

我设法创建满足目标且可重复使用的 "function" 的唯一方法是创建一个验证器:

def delete_orphaned_media_file(value):
    old_instance = value.instance.__class__.objects.get(pk = value.instance.pk)
    media_file_field = getattr(old_instance, value.field.name)

    if not media_file_field.name == value.name: media_file_field.delete()

它有效,但毕竟它是一个 "validator",一个 "validator" 应该验证一个字段,而不是 "that"。我的问题是,这样做是好的做法吗?

我的解决方案有更好的替代方案吗?但这种替代方案满足可重复使用的 objective。

任何帮助我学习的建议,谢谢。

其中一个问题是,两个或多个 FileField 可以引用同一个文件。在数据库中,FileField 存储文件的位置,因此两个或多个列可以有相同的文件,因此,仅删除旧文件并不(完全)安全。

例如,您可以使用 django-unused-media。你安装这个:

$ pip install <b>django-unused-media</b>

接下来将其添加到已安装的应用程序中:

# settings.py

INSTALLED_APPS = [
    # …,
    <b>'django_unused_media'</b>,
    # …
]

接下来可以运行:

python3 manage.py <b>cleanup_unused_media</b>

这将查找不再引用的文件,并以交互方式清理这些文件。

您还可以创建一个计划任务(例如 cron),运行 带有 --no-input 标志:

python3 manage.py cleanup_unused_media <b>--no-input</b>