Alessandro
Alessandro

Reputation: 643

Django 1.11: Flip image horizontally before saving into a Django model

I'm doing this application where I take a user's image and then flip it horizontally using ImageOps from the Pillow library. To do so I made a model like above:

from django.db import models


class ImageClient(models.Model):
    image = models.ImageField(null=False, blank=False)

I made a form using ImageField with a html form with enctype="multipart/form-data" and in my views I did the following:

from django.shortcuts import render, redirect
from .forms import ImageForm
from .models import ImageClient
from PIL import Image, ImageOps

def new(request):
    """
    Returns mirror image from client.
    """
    if request.method == 'POST':
        form = ImageForm(request.POST, request.FILES)
        if form.is_valid():
            image = Image.open(form.cleaned_data['image'])
            image = ImageOps.mirror(image)
            form_image = ImageClient(image=image)
            form_image.save()
            return redirect('img:detail', pk=form_image.id)
    else:
        form = ImageForm()
    return render(request, 'img/new_egami.html', {'form':form})
....

As you see, when a check if the form is valid, I open the form's image and flip it horizontally (using ImageOps.mirror()) then I save it. But I always getting this error 'Image' object has no attribute '_committed'. I know the Image object is from Pillow, but I do not understand this error. Can someone explain and/or solve this error?

Upvotes: 0

Views: 395

Answers (1)

birophilo
birophilo

Reputation: 962

The error is raised because the image is a PIL Image object, whereas Django is expecting and requires its own File object. You could save the Image object to an absolute file path and then refer to it, but there are more efficient ways here and here. Here is an adaptation of @madzohan's answer in the latter link for your image operation:

# models.py

from io import BytesIO
from django.core.files.base import ContentFile
from PIL import Image, ImageOps

class ImageClient(models.Model):
    image = models.ImageField(null=False, blank=False, upload_to="image/path/")

    def save(self, *args, **kwargs):
        pil_image_obj = Image.open(self.image)
        new_image = ImageOps.mirror(pil_image_obj)

        new_image_io = BytesIO()
        new_image.save(new_image_io, format='JPEG')

        temp_name = self.image.name
        self.image.delete(save=False)  

        self.image.save(
            temp_name,
            content=ContentFile(new_image_io.getvalue()),
            save=False
        )

        super(ImageClient, self).save(*args, **kwargs)

and views.py:

...
if form.is_valid():
    new_image = form.save()
    return redirect('img:detail', pk=new_image.id)

Upvotes: 2

Related Questions