Как передать Django InMemoryUploadFile в сериализатор в случае с изображениями?
Я создаю API с использованием Django Rest Framework для сохранения изображений из файла напрямую или из url. С первым случаем у меня проблем нет, а вот со вторым есть. Итак, мои модели выглядят следующим образом:
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
Далее, мой класс сериализатора:
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = [
'id',
'name',
'url',
'picture',
'width',
'height',
'parent_picture'
]
Основная идея заключается в том, что если вы передаете URL во время пост запроса, API должен загрузить изображение с этого URL и сохранить его в базе данных.
Для этого я переписал create
метод в классе Django Rest Framework ModelViewSet
:
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)
Но когда я использовал Django shell, я обнаружил, что сериализатор не сериализует InMemoryUploadFile
, только URL:
>>>django_file = InMemoryUploadFile(...)
>>>serializer = ImageSerializer(data={"url": url, "picture": django_file})
>>>serializer.data
{'url': 'some url', 'picture': None, 'parent_picture': None}
Но если я буду сохранять картинку через модель напрямую, то все будет нормально. Вот код, как я это делаю в конце файла views.py
:
Image.objects.create(url=url, picture=django_file)
return Response("success!")
Итак, я хочу знать, в чем проблема и как я могу ее решить?
Я нашел эту ссылку:
Поврежденное изображение для сериализатора
Итак, когда я добавил
...
django_file.seek(0)
serializer = self.get_serializer(data={"url": url, "picture": django_file})
...
ваш код сработал на моей машине.