Django переопределяет метод сохранения, дублирует изображение при обновлении модели

#django #django-models

#django #django-модели

Вопрос:

когда я обновляю модель, изображения дублируются.

из django.db импортируйте модели из io, импортируйте BytesIO из PIL, импортируйте изображение из django.core.файлы импортируют файл

 #image compression method
def compress(image):
    im = Image.open(image)
    im_io = BytesIO() 
    im.save(im_io, 'JPEG', quality=60) 
    new_image = File(im_io, name=image.name)
    return new_image

class PhotoGallery(models.Model):
    image_caption = models.CharField(max_length=50, null=True, blank=True)
    image = models.ImageField(upload_to='gallery')

#calling image compression function before saving the data
    def save(self, *args, **kwargs):
        new_image = compress(self.image)
        self.image = new_image
        super().save(*args, **kwargs)

def __str__(self):
    return self.image_caption
  

Ответ №1:

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

Вы можете обработать это с помощью сигналов

 import os

from django.db import models
from django.dispatch import receiver

@receiver(models.signals.pre_save, sender=PhotoGallery)
def auto_delete_image_on_change(sender, instance, **kwargs):
  """ Delete old image from HD on image update"""
  if not instance.pk:
    return False

  try:
    pg = PhotoGallery.objects.get(pk=instance.pk)
    old_image = pg.image if pg.image else None
  except PhotoGallery.DoesNotExist:
    return False

  new_image = instance.image
  if old_image and not old_image == new_image:
    if os.path.isfile(old_image.path):
      os.remove(old_image.path)

@receiver(models.signals.post_delete, sender=PhotoGallery)
def auto_delete_image_on_delete(sender, instance, **kwargs):
  """ Delete image from HD on image delete """
  if instance.image:
    if os.path.isfile(instance.image.path):
      os.remove(instance.image.path)
  if instance.qr_code:
    if os.path.isfile(instance.qr_code.path):
      os.remove(instance.qr_code.path)
  

Ответ №2:

Каждый раз, когда вы нажимаете save , метод сохранения всегда запускает следующее:

 #calling image compression function before saving the data
    def save(self, *args, **kwargs):
        new_image = compress(self.image)
        self.image = new_image
        super().save(*args, **kwargs)
  

Что означает, что при каждом нажатии он дублирует все больше и больше.

Я предлагаю проверять значение поля перед выполнением операций сжатия изображения. Но проблема в том, что вы должны использовать signals вместо обычного сохранения. Потому что, когда вы используете метод сохранения по умолчанию / переопределенный. Состояние изменений вашего поля не записывается. Итак, для этого мы должны сделать pre_save и post_save сигнал для проверки, было ли изображение изменено после сжатия.

В signals.py

 # ! These Variables are required to save instances.
pre_save_imageName = None # or ""
post_save_state_imageName = None # or ""

def has_image_saved(sender, instance, **kwargs):
    # Refer to these variables to save pre_save and post_save states.
    global pre_save_imageName, post_save_state_imageName

    # We could crunch this signal for use with pre_save and post_save into one callable function.
    if kwargs["action"] == "pre_save":
        # You could use path or filename here. Know what getattr arguments are.
        # The 3rd argument is None, the variable will be set to `None` when the function cannot find the value of the attribute.
        pre_save_imageName = getattr(instance, "image", None)

    if kwargs["action"] == "post_save":
        if pre_save_imageName:
            if pre_save_imageName != post_save_imageName:
                # *do your compression here*
            else:
                return
  

В models.py

 # ! Import your signals.py as well! To bind signal function of this model.
from django.db.models.signals import pre_save, post_save

class PhotoGallery(models.Model):
    image_caption = models.CharField(max_length=50, null=True, blank=True)
    image = models.ImageField(upload_to='gallery')

# Bind signals.
pre_save.connect(has_image_saved, sender=PhotoGallery)
post_save.connect(has_image_saved, sender=PhotoGallery)
  

Примечания:

  1. Вы могли бы удалить сжатое изображение здесь, если вам кажется, что оно было заменено.
  2. Если вы запутались, в сигналах instance эквивалентно self . Так что замена его не будет такой уж запутанной.