I am attempting to create a model with an image input that is to be resized and hashed on save. However, I appear to have created an infinite recursive call. I am basing the code for my resize_and_hash_image() method off of this answer to a previous question of mine. Is there a way I can save the image without calling self.instance.save()? Any help is appreciated.
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'
Console Output
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