Django - Невозможно удалить старый файл при изменении объекта с помощью сигналов - PullRequest
1 голос
/ 05 февраля 2020

У меня есть следующий сигнал для удаления старого postcover и postcover_tn (thumbnail) с моего жесткого диска. Это работает нормально, если я просто удаляю файлы через форму и вызываю save (), но если я хочу перезаписать старые файлы новыми, которые я загружаю, старые все еще на моем fs, есть идеи, как это исправить? :

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 генерируется при сохранении () сообщения, просто, если вам интересно об этом.

Ответы [ 2 ]

1 голос
/ 06 февраля 2020

У меня это работает так:

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)

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
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)

@ mfonism спасибо за ваши советы, они действительно помог мне понять.

0 голосов
/ 05 февраля 2020

Вот в чем проблема

Поскольку вы обрабатываете сигнал после сохранения , данные об экземпляре уже были вставлены в базу данных до выполнения обработчика сигнала.

Это означает, что sender.objects.get(pk=instance.pk).postcover и instance.postcover в приведенном выше коде извлекают одно и то же - недавно сохраненное посткрытие.

Итак, то, что вы называете old_postcover в своем коде, на самом деле новое сообщение . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}} Теперь, когда вы полностью переписаны , *1015* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 10 '* * * * * 10 *

*. ...

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 действительно одно и то же.


Как это исправить?

Вы можете использовать обработчик сигнала pre save .

В обработчике вы извлекаете old postcover из базы данных с помощью sender.objects.get(pk=instance.pk).postcover (после проверки , как вы это сделали в своем коде, чтобы убедиться, что экземпляр действительно имеет pk).

Затем вы удаляете это старое postcover, и все готово.


Проблема с это решение

Проблема, которую я сразу вижу при переходе по этому маршруту, заключается в том, что вы удаляете старые данные, не зная, будут ли новые данные приниматься базой данных.


Но если смотреть на светлую сторону

Тем не менее, если вы когда-нибудь будете менять пост-обложки через * 105 0 * s, вызов метода is_valid() в форме выполняет всю проверку экземпляра, поэтому вы можете быть уверены, что в момент выполнения обработчика новые данные в экземпляре были проверены и будут приняты база данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...