如果有图像,如何将 Django InMemoryUploadFile 传递给序列化程序?
How to pass Django InMemoryUploadFile to serializer in case with images?
我正在创建一个 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:
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = [
'id',
'name',
'url',
'picture',
'width',
'height',
'parent_picture'
]
主要思想是,如果您在 post 请求时传递 URL,API 需要从此 URL 下载图像并将其存储在数据库中。
为此,我重写了 Django Rest Framework 中的 create
方法 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)
但是当我使用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!")
所以,我想知道问题出在哪里,我该如何解决?
我找到这个 link:
Pillow corrupt image for serializer
所以,当我添加
...
django_file.seek(0)
serializer = self.get_serializer(data={"url": url, "picture": django_file})
...
你的代码在我的机器上运行。
我正在创建一个 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:
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = [
'id',
'name',
'url',
'picture',
'width',
'height',
'parent_picture'
]
主要思想是,如果您在 post 请求时传递 URL,API 需要从此 URL 下载图像并将其存储在数据库中。
为此,我重写了 Django Rest Framework 中的 create
方法 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)
但是当我使用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!")
所以,我想知道问题出在哪里,我该如何解决?
我找到这个 link:
Pillow corrupt image for serializer
所以,当我添加
...
django_file.seek(0)
serializer = self.get_serializer(data={"url": url, "picture": django_file})
...
你的代码在我的机器上运行。