尝试保存图像时出现RecursionError

时间:2020-03-10 00:07:13

标签: python django

我正在尝试使用图像输入创建模型,该图像输入将被调整大小并在保存时进行哈希处理。但是,我似乎创建了一个无限的递归调用。我将this answer to a previous question of mineresize_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 :(得分:0)

我知道问题出在哪里。 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()中依次调用。