Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'JpegImageFile' object has no attribute '_committed' error when using PIL

I'm using PIL to compress uploaded images(FileField). However I'm getting an error which I believe is a problem of double saving? (saving my image, and then saving the whole form which includes the image). I wanted to perform commit=False when I'm saving the image but it doesn't appear it's possible. Here's my code:

...
if form_post.is_valid():
    instance = form_post.save(commit=False)
    instance.user = request.user

if instance.image:
    filename = instance.image
    instance.image = Image.open(instance.image)
    instance.image.thumbnail((220, 130), Image.ANTIALIAS)
    instance.image.save(filename, quality=60)

instance.save()

returns 'JpegImageFile' object has no attribute '_committed' error on the last line (instance.save())

Can someone identify the problem? - and any idea how I can fix it?

Full Traceback:

File "/Users/zorgan/Desktop/app/lib/python3.5/site-packages/django/core/handlers/exception.py" in inner
  41.             response = get_response(request)

File "/Users/zorgan/Desktop/app/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/Users/zorgan/Desktop/app/lib/python3.5/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/Users/zorgan/Desktop/app/lib/python3.5/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  23.                 return view_func(request, *args, **kwargs)

File "/Users/zorgan/Desktop/project/site/post/views.py" in post
  68.                 if uploaded_file_type(instance) is True:

File "/Users/zorgan/Desktop/project/site/functions/helper_functions.py" in uploaded_file_type
  12.     f = file.image.read(1024)

Exception Type: AttributeError at /post/
Exception Value: 'JpegImageFile' object has no attribute 'read'

Full models:

class Post(models.Model):
    user = models.ForeignKey(User, blank=True, null=True)
    title = models.TextField(max_length=95)
    image = models.FileField(null=True, blank=True)

and the accompanying PostForm:

class PostForm(forms.ModelForm):
    title = forms.TextInput(attrs={'placeholder': 'title'})

    class Meta:
        model = Post
        fields = [
            'user',
            'title',
            'image',
        ]

views.py

def post(request):    
    if request.user.is_authenticated():
        form_post = PostForm(request.POST or None, request.FILES or None)
        if form_post.is_valid():
            instance = form_post.save(commit=False)

            if instance.image:
                filename = instance.image
                instance.image = Image.open(instance.image)
                instance.image.thumbnail((220, 130), Image.ANTIALIAS)
                instance.image.save(filename, quality=60)

            instance.save()

            return HttpResponseRedirect('/home/')
        else:
            form_post = PostForm()

        context = {
            'form_post': form_post,
        }

        return render(request, 'post/post.html', context)
    else:
        return HttpResponseRedirect("/accounts/signup/")

This following code:

if instance.image:
    im = Image.open(instance.image)
    print("Filename:", im.filename) #doesn't print anything
    thumb = im.thumbnail((220, 130), Image.ANTIALIAS)
    thumb.save(im.filename, quality=60)

returns an AttributeError : 'NoneType' object has no attribute 'save'. I believe this is because im.filename doesn't print anything. Any idea why?

The other method:

if instance.image:
    im = Image.open(instance.image)
    thumb = im.thumbnail((220, 130), Image.ANTIALIAS)
    thumb_io = BytesIO()
    thumb.save(thumb_io, im.format, quality=60)
    instance.image.save(im.filename, ContentFile(thumb_io.get_value()), save=False)

also returns AttributeError : 'NoneType' object has no attribute 'save', on this line: thumb.save(thumb_io, im.format, quality=60). Not sure why though?

like image 825
Zorgan Avatar asked Mar 12 '18 02:03

Zorgan


1 Answers

You must pass an instance of django's File object to FileField.save() to change the content of a file field. It works a bit differently from other types of model fields.

FieldFile.save(name, content, save=True)

This method takes a filename and file contents and passes them to the storage class for the field, then associates the stored file with the model field. If you want to manually associate file data with FileField instances on your model, the save() method is used to persist that file data.

from PIL import Image
from django.core.files.base import ContentFile

if instance.image:
    im = Image.open(instance.image)
    im.thumbnail((220, 130), Image.ANTIALIAS)
    thumb_io = BytesIO()
    im.save(thumb_io, im.format, quality=60)
    instance.image.save(im.filename, ContentFile(thumb_io.getvalue()), save=False)
instance.save()

But if you are not using a remote file storage backend, you could just overwrite the file itself. The file was created when you called form.save(). Since you are using the same filename and location, you don't really have to touch the model or tell django that you are messing with the file itself.

if instance.image:
    im = Image.open(instance.image)
    im.thumbnail((220, 130), Image.ANTIALIAS)
    im.save(im.filename, quality=60)
like image 198
Håken Lid Avatar answered Nov 11 '22 19:11

Håken Lid