Django метод сохранения сталкивается с сигналом об изменении модели - PullRequest
0 голосов
/ 05 февраля 2020

Я хочу сохранить посткрытие и соответствующий миниатюру посткрытия в меньшем разрешении. В конечном итоге пользователи также могут изменить посткрытие. Для достижения sh правильной обработки файлов я работаю с model.signals. кажется, что это работает довольно хорошо, пока я не попытаюсь изменить посткрытие моего объекта Post.

Когда я пытаюсь сохранить объект Post с измененным посткрытием, я получаю следующую ошибку:

 ValueError: The 'postcover' attribute has no file associated with it.

Первоначальное создание и удаление объекта Post работает, как и ожидалось.

models.py

class Post(models.Model):
...
    postcover = models.ImageField(
        verbose_name="Post Cover",
        blank=True,
        null=True,
    )
    postcover_tn = models.ImageField(
        verbose_name="Post Cover Thumbnail",
        blank=True,
        null=True,
    )
...

def save(self, *args, **kwargs):
    super(Post, self).save(*args, **kwargs)
    if self.postcover:
        if os.path.exists(self.postcover.path):
            image = Image.open(self.postcover)
            outputIoStream = BytesIO()
            baseheight = 600
            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 = 100
            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.post_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 False

    new_postcover = instance.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)

У меня также есть опция удаления в postcover мой forms.py, который может повлиять на этот процесс:

        if self.instance.postcover:
            self.fields['remove_cover'].disabled = False
            self.fields['postcover'].label = mark_safe('Overwrite Cover:')
            self.fields['remove_cover'].label = mark_safe('Remove Cover:')
        else:
            self.fields['postcover'].label = mark_safe('Cover:')
            del self.fields['remove_cover']


    def save(self, commit=True):
        instance = super(PostForm, self).save(commit=False)
        if self.cleaned_data.get('remove_cover'):
            try:
                os.unlink(instance.postcover.path)
            except OSError:
                pass
            instance.postcover = None
        if self.cleaned_data.get('remove_attachment'):
            try:
                os.unlink(instance.postattachment.path)
            except OSError:
                pass
            instance.postattachment = None
        if commit:
            instance.save()
        return instance

1 Ответ

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

Вы должны использовать сигнал post_save. Поскольку вы используете pre_save сейчас, ваш обработчик сигнала вызывается перед сохранением объекта в БД. Измените свой код на это:

@receiver(models.signals.post_save, sender=Post)
def post_auto_delete_files_on_change(sender, instance, **kwargs):
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...