I'm creating an API using Django Rest Framework to save images from file directly or from url. I've got no problem with first case but with second. So, my models looks like this:
class Image(models.Model):
picture = models.ImageField(upload_to="site_media", blank=True)
url = models.URLField(null=True, blank=True, max_length=255)
parent_picture = models.IntegerField(null=True, blank=True)
@property
def name(self):
return os.path.split(self.picture.name)[-1]
@property
def width(self):
return self.picture.width
@property
def height(self):
return self.picture.height
Next, my serializer class:
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = [
'id',
'name',
'url',
'picture',
'width',
'height',
'parent_picture'
]
The main idea is if you pass the URL while post request, API needs to download image from this URL and store it in database.
To do this I overwrote create
method in Django Rest Framework ModelViewSet
class:
from io import BytesIO
import urllib
import os
from django.core.files.uploadedfile import InMemoryUploadedFile
from PIL import Image as PILImage
from rest_framework import status
from rest_framework import viewsets
from rest_framework.response import Response
from .models import Image
from .serializers import ImageSerializer
class ImageViewSet(viewsets.ModelViewSet):
serializer_class = ImageSerializer
queryset = Image.objects.all()
def create(self, request, *args, **kwargs):
if request.data["url"]:
url = request.data["url"]
image_extensions = {
".jpg": "JPEG",
".jpeg": "JPEG",
".png": "PNG",
".gif": "GIF"
}
image_path = urllib.parse.urlparse(url).path
image_filename = image_path.split("/")[-1]
extension = os.path.splitext(image_filename)[-1]
image_bytes = BytesIO(urllib.request.urlopen(url).read())
pillow_image = PILImage.open(image_bytes)
buffer = BytesIO()
pillow_image.save(buffer, format=image_extensions[extension])
django_file = InMemoryUploadedFile(
buffer,
"ImageField",
image_filename,
"image/" extension[1:],
len(buffer.getvalue()),
None
)
serializer = self.get_serializer(data={"url": url, "picture": django_file})
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
But when I used Django shell I found that serializer doesn't serialize InMemoryUploadFile
, only URL:
>>>django_file = InMemoryUploadFile(...)
>>>serializer = ImageSerializer(data={"url": url, "picture": django_file})
>>>serializer.data
{'url': 'some url', 'picture': None, 'parent_picture': None}
But if I will save picture through the model directly, all will be okay. Here is the code how I do this in the end of views.py
file:
Image.objects.create(url=url, picture=django_file)
return Response("success!")
So, I want to know what is the problem and how can I fix it?
CodePudding user response:
I found this link:
Pillow corrupt image for serializer
So, when I added
...
django_file.seek(0)
serializer = self.get_serializer(data={"url": url, "picture": django_file})
...
your code worked on my machine.