3

I'm trying to save an image I download with requests and then edit with Pillow to ImageField in a model. But the object is being created without the image.

This is what I have:

settings.py

MEDIA_ROOT = BASE_DIR + "/media/"
MEDIA_URL = MEDIA_ROOT + "/magicpy_imgs/"

models.py

def create_path(instance, filename):
    path = "/".join([instance.group, instance.name])
    return path

class CMagicPy(models.Model):
    image = models.ImageField(upload_to=create_path)
    ....

    # Custom save method
    def save(self, *args, **kwargs):
        if self.image:
            image_in_memory = InMemoryUploadedFile(self.image, "%s" % (self.image.name), "image/jpeg", self.image.len, None)
            self.image = image_in_memory

        return super(CMagicPy, self).save(*args, **kwargs)

forms.py

class FormNewCard(forms.Form):
    imagen = forms.URLField(widget=forms.URLInput(attrs={'class': 'form-control'}))

views.py

def new_card(request):
    template = "hisoka/nueva_carta.html"

    if request.method == "POST":

        form = FormNewCard(request.POST)

        if form.is_valid():

            url_image = form.cleaned_data['imagen']
            group = form.cleaned_data['grupo']
            name = form.cleaned_data['nombre']
            description = form.cleaned_data['descripcion']

            answer = requests.get(url_image)
            image = Image.open(StringIO(answer.content))
            new_image = image.crop((22, 44, 221, 165))
            stringio_obj = StringIO()

            try:
                new_image.save(stringio_obj, format="JPEG")
                image_stringio = stringio_obj.getvalue()
                image_file = ContentFile(image_stringio)
                new_card = CMagicPy(group=group, description=description, name=name, image=image_file)
                new_card.save()

            finally:
                stringio_obj.close()

            return HttpResponse('lets see ...')

It creates the object but with no image. Please help. I've been trying to solve this for hours.

Alejandro Veintimilla
  • 10,743
  • 23
  • 91
  • 180

2 Answers2

4

Background

Though InMemoryUploadedFile is primarily intended to be used by MemoryFileUploadHandler, it can be used for other purposes as well. It should be noted that MemoryFileUploadHandler is for handling the situation when a user uploads a file to your server using a webform or a widget. However what you are handling is a situation the user merely provides a link and you download a file on to your web server.

Let us also recall that ImageFile is essentially a reference to a file stored on your filesystem. Only the name of the file is entered in the database and the file's content itself is stored in the storage system. Django allows you to specify different storage systems so that the file can be saved on the cloud if you need to.

Solution

All you need to do is to pass the content of the image that you generate using Pillow with a filename to ImageField. And that content can be sent through a InMemoryUploaded file or a ContentFile. However there isn't a need to use both.

So this is your model.

class CMagicPy(models.Model):
    image = models.ImageField(upload_to=create_path)

    # over ride of save method not needed here.

This is your view.

  try:
     # form stuff here

     answer = requests.get(url_image)

     image = Image.open(StringIO(answer.content))
     new_image = image.rotate(90) #image.crop((0, 0, 22, 22))
     stringio_obj = StringIO()


     new_image.save(stringio_obj, format="JPEG")
     image_file = InMemoryUploadedFile(stringio_obj, 
         None, 'somefile.jpg', 'image/jpeg',
         stringio_obj.len, None)

     new_card = CMagicPy()
     new_card.image.save('bada.jpg',image_file)
     new_card.save()

 except:
     # note that in your original code you were not catching
     # an exception. This is probably what made it harder for
     # you to figure out what the root cause of the problem was
     import traceback
     traceback.print_exc()
     return HttpResponse('error')
 else:
     return HttpResponse('done')

Footnotes

Exception handling has been added because things can and will go wrong.

Instead of using JPEG and image/jpeg you should use answers.headers['Content-type'] and choose the appropriate one.

e4c5
  • 52,766
  • 11
  • 101
  • 134
2

Try this self.image.save(some_file_path, ContentFile(image_stringio)). And it seems to me that you don't need to override save() in model.

Sergey Gornostaev
  • 7,596
  • 3
  • 27
  • 39
  • Thanks for you answer, it made me realize that it woudn't be possible to call `save` into the `URLField` +1 ... tried changing it to an `ImageField` but it doesn't make sense (and doesn't work) since the user submits a url that I'll use to request an image from another website. So ... I decided a `ModelForm` and a CBV didn't help me in this case, and changed everything to a Function Based View. It still doesn't save the image, but at least this code makes more sense to me. Any ideas? Tried with and without the custom save method in the model. – Alejandro Veintimilla May 12 '16 at 05:37