Как сохранить объект изображения подушки в Django ImageField?

Наличие модели Django для миниатюрного изображения, например:

class Thumb(models.Model):
    thumb = models.ImageField(upload_to='uploads/thumb/', null=True, default=None)

Представление создает эскиз с пакетом pillow и должен сохранить его в экземпляр Thumb, используя такой код:

image.thumbnail((50, 50))
inst.thumb.save('thumb.jpg', ???)

Как правильно сделать image данные для inst.thumb.save в ????

Мне удалось заставить работать следующее:

thumb_temp = NamedTemporaryFile()
image.save(thumb_temp, 'JPEG', quality=80)
thumb_temp.flush()
inst.thumb.save('thumb.jpg', File(thumb_temp))
thumb_temp.close()  # Probably required to ensure temp file delete at close

Но писать временный файл только для передачи внутренних данных в inst.thumb.save кажется довольно неуклюжим, поэтому мне интересно, есть ли более элегантный способ сделать это. Документация для класса Django NamedTemporaryFile.


person EquipDev    schedule 05.10.2015    source источник


Ответы (3)


Вот рабочий пример (Python3, django 1.11), который берет изображение из Model.ImageField, выполняет над ним операцию изменения размера с помощью PIL (Pillow), а затем сохраняет полученный файл в том же ImageField. Надеюсь, это должно быть легко адаптировать к любой обработке, которую вы должны выполнять с изображениями ваших моделей.

from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.files.base import ContentFile
from PIL import Image

IMAGE_WIDTH = 100
IMAGE_HEIGHT = 100

def resize_image(image_field, width=IMAGE_WIDTH, height=IMAGE_HEIGHT, name=None):
    """
    Resizes an image from a Model.ImageField and returns a new image as a ContentFile
    """
    img = Image.open(image_field)
    if img.size[0] > width or img.size[1] > height:
        new_img = img.resize((width, height))
    buffer = BytesIO()
    new_img.save(fp=buffer, format='JPEG')
    return ContentFile(buffer.getvalue())

#assuming your Model instance is called `instance`
image_field = instance.image_field
img_name = 'my_image.jpg'
img_path = settings.MEDIA_ROOT + img_name

pillow_image = resize_image(
                  image_field,
                  width=IMAGE_WIDTH,
                  height=IMAGE_HEIGHT,
                  name=img_path)

image_field.save(img_name, InMemoryUploadedFile(
     pillow_image,       # file
     None,               # field_name
     img_name,           # file name
     'image/jpeg',       # content_type
     pillow_image.tell,  # size
     None)               # content_type_extra
)
person Escher    schedule 27.08.2017
comment
это вызывает ошибку "чтение" атрибута "Объект изображения" - person Yosef Salmalian; 12.05.2021
comment
@YosefSalmalian, вам, вероятно, следует задать новый вопрос, так как read не вызывается PIL.Image в коде этого ответа. - person Escher; 13.05.2021

Вы можете создать приемник pre_save для своей модели:

from io import BytesIO
from functools import partial
from django.db import models
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image

class Article(models.Model):
    title = models.CharField(max_length=120)
    slug = models.SlugField(max_length=120, unique=True)
    image = models.ImageField(
        upload_to=upload_image_location
    )
    thumbnail_image = models.ImageField(
        upload_to=partial(upload_image_location, thumbnail=True), 
        editable=False, blank=True
    )

    def create_thumbnail(self):
        image = Image.open(self.image.file.file)
        image.thumbnail(size=(310, 230))
        image_file = BytesIO()
        image.save(image_file, image.format)
        self.thumbnail_image.save(
            self.image.name,
            InMemoryUploadedFile(
                image_file,
                None, '',
                self.image.file.content_type,
                image.size,
                self.image.file.charset,
            ),
            save=False
        )

@receiver(models.signals.pre_save, sender=Article)
def prepare_images(sender, instance, **kwargs):
    if instance.pk:
        try:
            article = Article.objects.get(pk=instance.pk)
            old_image = article.image
            old_thumbnail_image = article.thumbnail_image
        except Article.DoesNotExist:
            return
        else:
            new_image_extension = os.path.splitext(instance.image.name)[1]
            if old_image and not old_image.name.endswith(new_image_extension):
                old_image.delete(save=False)
                old_thumbnail_image.delete(save=False)

    if not instance.thumbnail_image or not instance.image._committed:
        instance.create_thumbnail()

Миниатюра изображения создается методом create_thumbnail с использованием Pillow. Этот метод отлично работает с django-storages и сохраняет миниатюру в пользовательском хранилище с именем, например, 'article_slug_thumbnail.jpeg. '.

Мой метод upload_image_location:

def upload_image_location(instance, filename, thumbnail=False):
    _, ext = os.path.splitext(filename)
    return f'articles/{instance.slug}{f"_thumbnail" if thumbnail else ""}{ext}'
person QuadX    schedule 22.10.2018

person    schedule
comment
Было бы очень полезно добавить контекст и объяснение к вашему ответу. - person FloLie; 19.05.2021