如何在django ModelForm中调整图像大小?

时间:2018-02-22 17:53:12

标签: python django python-3.x django-forms python-imaging-library

我有以下ModelForm

class MyModelForm(ModelForm):

    def __init__(self, *a, **kw):
        super().__init__(*a, **kw)
        self.fields['image'].validators = []

    def clean_image(self):
        img = self.cleaned_data.get('image')
        if not img:
            return img
        # Resize to Max 1024x1024 px
        new_size = (1024, 1024)
        if any(x>new_size[0] for x in img.image.size):
            img.image.thumbnail(new_size)
        return img

当我尝试上传图片时会抛出错误:

  

异常类型:/ some / view的AttributeError   异常值:'NoneType'对象没有属性'read'

完整追溯:

Traceback:

File "/usr/local/lib/python3.6/site-packages/PIL/ImageFile.py" in load
  147.             read = self.load_read

During handling of the above exception ('JpegImageFile' object has no attribute 'load_read'), another exception occurred:

File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/opt/django/portal/views/advert.py" in post
  167.         if not all([f.is_valid() for f in (usr_form, adv_form)]):

File "/opt/django/portal/views/advert.py" in <listcomp>
  167.         if not all([f.is_valid() for f in (usr_form, adv_form)]):

File "/usr/local/lib/python3.6/site-packages/django/forms/forms.py" in is_valid
  183.         return self.is_bound and not self.errors

File "/usr/local/lib/python3.6/site-packages/django/forms/forms.py" in errors
  175.             self.full_clean()

File "/usr/local/lib/python3.6/site-packages/django/forms/forms.py" in full_clean
  384.         self._clean_fields()

File "/usr/local/lib/python3.6/site-packages/django/forms/forms.py" in _clean_fields
  405.                     value = getattr(self, 'clean_%s' % name)()

File "/opt/django/portal/forms.py" in clean_image
  57.             img.image.thumbnail(new_size)

File "/usr/local/lib/python3.6/site-packages/PIL/Image.py" in thumbnail
  2008.         im = self.resize(size, resample)

File "/usr/local/lib/python3.6/site-packages/PIL/Image.py" in resize
  1697.         self.load()

File "/usr/local/lib/python3.6/site-packages/PIL/ImageFile.py" in load
  151.             read = self.fp.read

Exception Type: AttributeError at /some/view
Exception Value: 'NoneType' object has no attribute 'read'

我想这是因为ImageFielddjango的{​​{3}}是image = Image.open(file) # verify() must be called immediately after the constructor. image.verify() # Annotating so subclasses can reuse it for their own validation f.image = image

verify()

根据一些implemented我应该在调用|后重新打开图像文件,但我不知道我该怎么办?或者这是否是解决问题的最佳方法?< / p>

1 个答案:

答案 0 :(得分:0)

Django ImageField默认情况下会验证上传的文件是否包含有效图片,实际上它会将图像数据移交给PIL进行处理。可以将ImageField子类化并覆盖其to_python方法,以便在验证的第一步应用resize过滤器,但是实现自定义clean_*方法更方便,这是一个示例:< / p>

class MyModel(models.Model):
    image = models.ImageField(upload_to='images/%Y/%m/%d/', blank=True, null=True)


class MyModelForm(ModelForm):

    def clean_image(self):
        img = self.cleaned_data.get('image')
        if not img:
            return img
        maxdim = 1024
        if any(dim > maxdim for dim in img.image.size):
            # Resize too large image up to the max_size
            from PIL import Image
            i = Image.open(img.file)
            fmt = i.format.lower()
            i.thumbnail((maxdim, maxdim))
            # We must reset io.BytesIO object, otherwise resized image bytes
            # will get appended to the original image  
            img.file = type(img.file)()
            i.save(img.file, fmt)
        return img

    class Meta:
        model = models.MyModel

注意事项:

  • 原始图片bytes存储在img.file属性
  • img.fileio.BytesIO
  • 的一个实例
  • PIL.Image orig的实例。图像存储在img.image属性