从模型中删除孤立文件
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
的函数,代码重复,这根本不是最优的。
我试图在 ImageField
和 FileField
等各个领域创建一个满足 class_files_custom_upload
函数目标的可重用函数,但我无法做到,因为函数只接收instance
和filename
2个参数,数据太少无法实现。
我设法创建满足目标且可重复使用的 "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>
我有以下型号:
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
的函数,代码重复,这根本不是最优的。
我试图在 ImageField
和 FileField
等各个领域创建一个满足 class_files_custom_upload
函数目标的可重用函数,但我无法做到,因为函数只接收instance
和filename
2个参数,数据太少无法实现。
我设法创建满足目标且可重复使用的 "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>