为了账号安全,请及时绑定邮箱和手机立即绑定

Django - 无法使用信号删除对象更改的旧文件

Django - 无法使用信号删除对象更改的旧文件

一只斗牛犬 2022-07-19 15:24:48
我有以下信号可以从我的硬盘中删除旧的 postcover 和 postcover_tn(缩略图)。如果我只是通过表单删除文件并调用 save() ,这工作正常,但是如果我想用新文件覆盖旧文件,我上传的旧文件仍在我的 fs 上,知道如何解决这个问题吗? :signals.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() 上生成的,如果你想知道的话。
查看完整描述

2 回答

?
四季花海

TA贡献1811条经验 获得超5个赞

我让它像这样工作:


models.py:


def save(self, *args, **kwargs):

    super(Post, self).save(*args, **kwargs)

    if self.postcover:

        if not (self.postcover_tn and os.path.exists(self.postcover_tn.path)):

            image = Image.open(self.postcover)

            outputIoStream = BytesIO()

            baseheight = 400

            hpercent = baseheight / image.size[1]

            wsize = int(image.size[0] * hpercent)

            imageTemproaryResized = image.resize((wsize, baseheight))

            imageTemproaryResized.save(outputIoStream, format='PNG')

            outputIoStream.seek(0)

            self.postcover = InMemoryUploadedFile(outputIoStream, 'ImageField',

                                                  "%s.png" % self.postcover.name.split('.')[0], 'image/png',

                                                  sys.getsizeof(outputIoStream), None)

            image = Image.open(self.postcover)

            outputIoStream = BytesIO()

            baseheight = 175

            hpercent = baseheight / image.size[1]

            wsize = int(image.size[0] * hpercent)

            imageTemproaryResized = image.resize((wsize, baseheight))

            imageTemproaryResized.save(outputIoStream, format='PNG')

            outputIoStream.seek(0)

            self.postcover_tn = InMemoryUploadedFile(outputIoStream, 'ImageField',

                                                  "%s.png" % self.postcover.name.split('.')[0], 'image/png',

                                                  sys.getsizeof(outputIoStream), None)

    elif self.postcover_tn:

        self.postcover_tn.delete()


    super(Post, self).save(*args, **kwargs)

signals.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

new_postcover_tn = instance.postcover_tn

if not old_postcover == new_postcover:

    if os.path.isfile(old_postcover.path):

        os.remove(old_postcover.path)

        if old_postcover_tn == new_postcover_tn:

            if os.path.isfile(old_postcover_tn.path):

                os.remove(old_postcover_tn.path)


查看完整回答
反对 回复 2022-07-19
?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

这是问题所在

由于您正在处理保存后信号,因此在信号处理程序执行之前,实例上的数据已经插入到数据库中。


这意味着在你上面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确实是一回事。


如何解决这个问题?

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


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


然后你删除这个旧的postcover,你就完成了。


此解决方案的问题

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


但看好的一面

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


查看完整回答
反对 回复 2022-07-19
  • 2 回答
  • 0 关注
  • 76 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信