我有以下信号从硬盘上删除旧的postcover和postcover_tn(缩略图)。如果我只是删除表单中的文件并调用save(),则此方法工作正常,但如果我想用新文件覆盖旧文件,而我上传的旧文件仍在我的文件系统中,您知道如何解决此问题吗? :
signal.py
@receiver(models.signals.pre_save, sender=Post)
def post_auto_delete_files_on_change(sender, instance, **kwargs):
"""
Deletes old file from filesystem
when corresponding object is updated
with new file.
"""
if not instance.pk:
return False
try:
old_postcover = sender.objects.get(pk=instance.pk).postcover
old_postcover_tn = sender.objects.get(pk=instance.pk).postcover_tn
except sender.DoesNotExist:
return False
if not old_postcover:
return
new_postcover = instance.postcover
if not old_postcover == new_postcover:
if os.path.isfile(old_postcover.path):
os.remove(old_postcover.path)
new_postcover_tn = instance.postcover_tn
if not old_postcover_tn == new_postcover_tn:
if os.path.isfile(old_postcover.path):
os.remove(old_postcover.path)
postcover_tn在Post的save()上生成,即使您可能对此有所怀疑。
最佳答案
这是问题
由于您正在处理后保存信号,因此在执行信号处理程序之前,实例上的数据已插入到数据库中。
这意味着您上面的代码中的sender.objects.get(pk=instance.pk).postcover
和instance.postcover
获取相同的内容-新保存的postcover。
因此,您要在代码中命名old_postcover
的东西实际上是新的postcover。真正的旧后盖已被永久覆盖,并且仍在您的文件系统上。
离题
现在,这部分代码的主体...
if not old_postcover == new_postcover:
if os.path.isfile(old_postcover.path):
os.remove(old_postcover.path)
os.remove(old_postcover_tn.path)
...永远不会运行,因为
old_postcover
和new_postcover
确实是同一回事。如何解决这个问题?
您可以使用预保存信号处理程序。
在处理程序中,您可以使用
sender.objects.get(pk=instance.pk).postcover
从数据库中获取旧的postcover(在检查之后,就像在代码中所做的那样,以确保实例确实具有pk)。然后,删除此旧的postcover,即可完成。
该解决方案的问题
我沿这条路线可以立即看到的问题是,您正在删除旧数据,而首先不知道数据库是否会接受新数据。
但是光明的一面
但是,如果仅通过
ModelForm
更改后发现,则对表单上is_valid()
方法的调用将对实例执行所有验证,因此您可以确信在处理程序执行时,实例上的新数据已经过验证,将被数据库接受。