@gh0sty
Веб-разработчик. Пишу под Python Django.

Как создать thumbnail pillow image после сохранения в модели django?

Задача в том, чтоб загрузить изображение и создать thumbnail через Pillow.
Порядок действий такой:
  1. Беру пользовательское изображение
  2. Создаю объект модели Photo
  3. Заполняю данные кроме самого фото
  4. Через Pillow изменяю размер и преобразовываю blob в jpg
  5. Вызываю метод save у модели Photo
  6. Метод save создает путь для загрузки
  7. Метод save должен создавать thumbnail после super save (чего не происходит)


Я пробовал через post_save - ничего.

Вот views:
...

if photo_upload_form.is_valid():
    img_name = 'template{}-{}.jpg'.format(template.id, timezone.now().strftime('%Y-%m-%d-%H-%M-%S'))
    ph = Photo(user=profile.user, order=request.POST.get('files_uploaded'))
    im = Image.open(photo_upload_form.cleaned_data['image'])
    im = im.convert('RGB')
    blob = BytesIO()
    im.save(blob, 'JPEG', quality=100)
    if im.width > 1920 or im.height > 1080:
        output_size = (1920, 1080)
        im.thumbnail(output_size)
        im.save(blob, 'JPEG', quality=100)
    ph_name = 'template{}-{}.jpg'.format(template.id, timezone.now().strftime('%Y-%m-%d-%H-%M-%S'))
    ph.name = ph_name
    ph.slug = slugify(ph_name)
    print('1')
    ph.image.save(ph_name, File(blob), save=False)
    print('2')
    ph.save()
    print('3')

...


Вот save:
def save(self, *args, **kwargs):
    print('save')
    self.image.name = get_path_upload_image(self.image.name, self.user.username)
    print('path_got')
    super().save(*args, **kwargs)
    print('saved')
    path = self.image.path
    print(path)
    generate_thumb(Photo, path, **kwargs)
    print('generated')


Вот get_path_upload_image и generate_thumb:
def get_path_upload_image(file, nick):
    """
    Переопределение имени и путя фотографии, сокращение названия
    В следующий формат: (media)/photos/username/2019-08-20/photo-name_23-59-59.extension
    """
    time = timezone.now().strftime('%Y-%m-%d')
    end_extension = file.split('.')[-1]
    head = file.split('.')[0]
    if len(head) > 30:
        head = head.replace('.', '')[:30]
    file_name = head + '_' + timezone.now().strftime('%H-%M-%S') + '.' + end_extension
    path = os.path.join('photos', '{}', '{}', '{}').format(nick, time, file_name)
    return path

#@receiver(post_save, sender=Photo) тож не катит
def generate_thumb(sender, path, **kwargs):
    print('gen')
    # Генерация thumbnail
    if path:
        img = Image.open(path)
        thumb_root_path = '/'.join(path.split('/')[:-1])
        thumb_name = path.split('/')[-1].split('.')[0] + '-thumb.'
        thumb_ext = path.split('/')[-1].split('.')[-1]
        thumb_path = thumb_root_path + thumb_name + thumb_ext
        output_size = (120, 80)
        img.thumbnail(output_size)
        img.save(thumb_path)


Терминал выводит:

1
2
save
path_got
saved
...\media\photos\gh0st\2019-11-02\template402-2019-11-02-14-54-0_14-54-02.jpg
gen
...
File "...\views.py", line 1055, in profile_create_ad_template_image_upload
ph.save()
File "...\models.py", line 331, in save
generate_thumb(Photo, path, **kwargs)
File "...\models.py", line 344, in generate_thumb
img = Image.open(path)
...
FileNotFoundError: [Errno 2] No such file or directory: '...\\media\\photos\\gh0st\\2019-11-02\\template402-2019-11-02-14-54-0_14-54-02.jpg'


Точки - от меня.
В media - объекта с таким именем действительно нет, как будто super save забивает на self.image.name.
А "\\" - это норма? Может проблема в экранировании?
Я часа 3 страдаю, хз че делать. Памагити)))
  • Вопрос задан
  • 967 просмотров
Решения вопроса 1
@gh0sty Автор вопроса
Веб-разработчик. Пишу под Python Django.
7 долбанных часов...
Это было дико, мать твою!


Подробности (не ручаюсь за инфу, мейби говнокодеры с форумов пишут вслепую):
  1. Значт так, в какой-то версии Django метод super save стал break-ать метод save модели
  2. В какой-то еще версии Django запретили создавать папки изменением self.image.name. Но разрешили в upload_to закидывать функцию с неявными (встроенными) аргументами без возможности добавления (!)
  3. Решил не париться save и post_save - просто создал внутренний метод и вызываю его перед super save.


Це мой идеальный модельный код:
def get_path_upload_image(instance, filename):
    """
    Переопределение имени и путя фотографии, сокращение названия
    В следующий формат: (media)/photos/username/2019-08-20/photo-name_23-59-59.extension
    """
    if '/' in filename:
        filename = filename.split('/')[-1]
    img_header, img_extension = os.path.splitext(filename)
    if len(img_header) > 30:
        if img_header[-6:] == '_thumb':
            img_header = img_header[:30] + '_thumb'
        else:
            img_header = img_header[:30]
    time = timezone.now().strftime('%Y-%m-%d')
    img_name = img_header + '_' + time + img_extension
    path = os.path.join('photos', '{}', '{}', '{}').format(instance.user.username, time, img_name)
    return path


class Photo(models.Model):
    """ Фото """
    user = models.ForeignKey(User, verbose_name='Пользователь', on_delete=models.CASCADE)
    name = models.CharField('Имя', max_length=50)
    image = models.ImageField('Фото', upload_to=get_path_upload_image, blank=True, null=True)
    thumbnail = models.ImageField('Превью', upload_to=get_path_upload_image, editable=False, blank=True, null=True)
    order = models.PositiveSmallIntegerField('Порядковый номер', null=True, blank=True)
    is_main = models.BooleanField('Главное', default=False)
    created = models.DateTimeField('Дата создания', auto_now_add=True)
    slug = models.SlugField('url', max_length=50, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = 'Изображение'
        verbose_name_plural = 'Изображения'

    def save(self, *args, **kwargs):
        self.make_thumbnail()
        super(Photo, self).save(*args, **kwargs)

    def make_thumbnail(self):
        image = Image.open(self.image)
        image.thumbnail((120, 80), Image.ANTIALIAS)  # ваш размер thumbnail
        thumb_name, thumb_extension = os.path.splitext(self.image.name)
        thumb_filename = thumb_name + '_thumb' + thumb_extension
        temp_thumb = BytesIO()
        image.save(temp_thumb, 'JPEG')
        temp_thumb.seek(0)
        self.thumbnail.save(thumb_filename, ContentFile(temp_thumb.read()), save=False)
        temp_thumb.close()


Це мой views:
ph = Photo(user=profile.user, order=request.POST.get('files_uploaded'))
im = Image.open(photo_upload_form.cleaned_data['image'])
im = im.convert('RGB')
blob = BytesIO()
if im.width > 1920 or im.height > 1080:
    output_size = (1920, 1080)
    im.thumbnail(output_size)
    im.save(blob, 'JPEG', quality=100)
ph_name = 'gallery{}-{}.jpg'.format(gal.id, timezone.now().strftime('%Y-%m-%d-%H-%M-%S'))
ph.name = ph_name
ph.slug = slugify(ph_name)
ph.image.save(ph_name, File(blob), save=False)
ph.save()


Если че - он сжимает полученное изображение до 1920x1080, закидывает его в photo и автоматически генерит thumbnail - да еще и генерируя путь в зависимости от даты и username.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы