我有以下信号从硬盘上删除旧的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).postcoverinstance.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_postcovernew_postcover确实是同一回事。



如何解决这个问题?

您可以使用预保存信号处理程序。

在处理程序中,您可以使用sender.objects.get(pk=instance.pk).postcover从数据库中获取旧的postcover(在检查之后,就像在代码中所做的那样,以确保实例确实具有pk)。

然后,删除此旧的postcover,即可完成。



该解决方案的问题

我沿这条路线可以立即看到的问题是,您正在删除旧数据,而首先不知道数据库是否会接受新数据。



但是光明的一面

但是,如果仅通过ModelForm更改后发现,则对表单上is_valid()方法的调用将对实例执行所有验证,因此您可以确信在处理程序执行时,实例上的新数据已经过验证,将被数据库接受。

08-03 14:39