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