RecursionError при попытке сохранить изображение - PullRequest
1 голос
/ 10 марта 2020

Я пытаюсь создать модель с входным изображением, размер которого должен быть изменен и сохранен при сохранении. Тем не менее, я, кажется, создал бесконечный рекурсивный вызов. Я строю код для моего resize_and_hash_image() метода из этого ответа на мой предыдущий вопрос . Есть ли способ сохранить изображение без вызова self.instance.save()? Любая помощь приветствуется.

models.py

import hashlib

from base64 import b16encode
from functools import partial
from io import BytesIO
from PIL import Image

from django.db import models

class Person(models.Model):
    prefix = models.CharField(blank=True, null=True, max_length=5)
    first_name = models.CharField(max_length=35)
    last_name = models.CharField(max_length=35)
    suffix = models.CharField(blank=True, null=True, max_length=5)
    image = models.ImageField(default=None, upload_to='people/')
    _image_hash = models.BinaryField(blank=True, null=True, default=None, max_length=16)
    bio = models.TextField()
    phone = models.CharField(max_length=10)
    email = models.EmailField()

    def __str__(self):
        return self.full_name

    def save(self, *args, **kwargs):
        if self.image:
            self.resize_and_hash_image()

        super().save(*args, **kwargs)

    def hash_file(self, file, block_size=65536):
        hasher = hashlib.md5()
        for buf in iter(partial(file.read, block_size), b''):
            hasher.update(buf)

        return hasher.digest()

    def resize_and_hash_image(self):
        img = Image.open(self.image).convert('RGB')
        width, height = img.size
        longest, shortest = 960, 720

        if (width >= height and (width > longest or height > shortest)) or (height > width and (height > longest or width > shortest)):
            if width > height:
                if (height * longest/ width) > shortest:
                    new_height = shortest
                    new_width = int(width * new_height / height)
                else:
                    new_width = longest
                    new_height = int(height * new_width / width)
            else:
                if (width * longest / height) > shortest:
                    new_width = shortest
                    new_height = int(height * new_width / width)
                else:
                    new_height = longest
                    new_width = int(width * new_height / height)

            img = img.resize((new_width, new_height), Image.ANTIALIAS)

        img_file = BytesIO()
        img.save(img_file, 'JPEG', quality=90)

        self._image_hash = self.hash_file(img_file)
        new_name = self.image_hash + '.jpg'
        self.image.save(new_name, img_file)

    @property
    def full_name(self):
        full_name = self.first_name + ' ' + self.last_name

        if self.prefix:
            full_name = self.prefix + ' ' + full_name

        if self.suffix:
            full_name = full_name + ' ' + self.suffix

        return full_name

    @property
    def display_phone(self):
        return '(%s) %s-%s' % \
            (self.phone[0:3], self.phone[3:6], self.phone[6:10]) \
            if self.phone else ''

    @property
    def image_hash(self):
        return str(b16encode(self._image_hash).lower(), 'utf-8')

    class Meta:
        verbose_name_plural = 'people'

Консольный вывод

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
March 09, 2020 - 18:52:05
Django version 2.2.6, using settings 'mtm.settings'
Starting development server at http://10.0.0.100:8000/
Quit the server with CONTROL-C.
Internal Server Error: /admin/home/person/add/
Traceback (most recent call last):
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/contrib/admin/options.py", line 606, in wrapper
    return self.admin_site.admin_view(view)(*args, **kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/contrib/admin/sites.py", line 223, in inner
    return view(request, *args, **kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/contrib/admin/options.py", line 1634, in add_view
    return self.changeform_view(request, None, form_url, extra_context)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/utils/decorators.py", line 45, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/utils/decorators.py", line 142, in _wrapped_view
    response = view_func(request, *args, **kwargs)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/contrib/admin/options.py", line 1522, in changeform_view
    return self._changeform_view(request, object_id, form_url, extra_context)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/contrib/admin/options.py", line 1561, in _changeform_view
    self.save_model(request, new_object, form, not add)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/contrib/admin/options.py", line 1088, in save_model
    obj.save()
  File "/home/matt/Repositories/mtm/home/models.py", line 44, in save
    self.resize_and_hash_image()
  File "/home/matt/Repositories/mtm/home/models.py", line 83, in resize_and_hash_image
    self.image.save(new_name, img_file)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/db/models/fields/files.py", line 93, in save
    self.instance.save()
  File "/home/matt/Repositories/mtm/home/models.py", line 44, in save
    self.resize_and_hash_image()
  File "/home/matt/Repositories/mtm/home/models.py", line 83, in resize_and_hash_image
    self.image.save(new_name, img_file)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/db/models/fields/files.py", line 93, in save
    self.instance.save()
  ...
  File "/home/matt/Repositories/mtm/home/models.py", line 44, in save
    self.resize_and_hash_image()
  File "/home/matt/Repositories/mtm/home/models.py", line 83, in resize_and_hash_image
    self.image.save(new_name, img_file)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/db/models/fields/files.py", line 93, in save
    self.instance.save()
  File "/home/matt/Repositories/mtm/home/models.py", line 44, in save
    self.resize_and_hash_image()
  File "/home/matt/Repositories/mtm/home/models.py", line 83, in resize_and_hash_image
    self.image.save(new_name, img_file)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/db/models/fields/files.py", line 93, in save
    self.instance.save()
  File "/home/matt/Repositories/mtm/home/models.py", line 44, in save
    self.resize_and_hash_image()
  File "/home/matt/Repositories/mtm/home/models.py", line 56, in resize_and_hash_image
    img = Image.open(self.image).convert('RGB')
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/Image.py", line 2804, in open
    im = _open_core(fp, filename, prefix)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/Image.py", line 2790, in _open_core
    im = factory(fp, filename)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/JpegImagePlugin.py", line 789, in jpeg_factory
    im = JpegImageFile(fp, filename)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/ImageFile.py", line 106, in __init__
    self._open()
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/JpegImagePlugin.py", line 376, in _open
    handler(self, i)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/JpegImagePlugin.py", line 67, in APP
    s = ImageFile._safe_read(self.fp, n)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/PIL/ImageFile.py", line 551, in _safe_read
    return fp.read(size)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/core/files/utils.py", line 16, in <lambda>
    read = property(lambda self: self.file.read)
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/db/models/fields/files.py", line 41, in _get_file
    self._require_file()
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/db/models/fields/files.py", line 37, in _require_file
    if not self:
  File "/home/matt/Repositories/mtm/env/lib/python3.5/site-packages/django/core/files/base.py", line 26, in __bool__
    return bool(self.name)
RecursionError: maximum recursion depth exceeded while calling a Python object

1 Ответ

0 голосов
/ 10 марта 2020

Я вижу, где проблема. Последний блок после img = img.resize((new_width, new_height), Image.ANTIALIAS) должен иметь такой отступ:

            img = img.resize((new_width, new_height), Image.ANTIALIAS)
            img_file = BytesIO()
            img.save(img_file, 'JPEG', quality=90)

            new_name = self.image.name.split('.')[0] + '.jpg'
            self.image.save(new_name, img_file)

Этот способ self.image.save() вызывается только один раз после изменения размера изображения в первый раз. Далее в моем списке разделение метода на отдельные методы resize_image() и hash_image(), которые будут вызываться последовательно в save().

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