英文:
Django REST error when serializing uploaded image
问题
我遇到了一个序列化器错误:
"上传有效的图片。您上传的文件不是图片或者是一个损坏的图片"
当尝试序列化一个上传的图片时
我的代码:
模型:
class Post(models.Model):
    text = models.TextField(max_length=10000)
class Image(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    image = models.ImageField(blank=False, null=False, upload_to='test')
视图:
class CreateNewPostAPIView(generics.CreateAPIView):
    serializer_class = serializers.NewPostSerializer
    def get_serializer_context(self):
        context = super().get_serializer_context()
        if self.request.data.get('image', None):
            context['image'] = self.request.data.pop('image', None)
        return context
序列化器:
class NewPostSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        post = Post.objects.create(**validated_data)
        image = self.context['image'][0] # 现在仅处理一个图片
        # 图片的类型是 <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
        serializer = ImageSerializer(data={'post': post.pk, 'image': image})
        is_valid = serializer.is_valid()  # 它总是为False!
        return post
class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = ('post', 'image')
我已经尝试过不使用序列化器来完成,以下是可以工作的代码:
class NewPostSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        post = Post.objects.create(**validated_data)
        image = self.context['image']
        Image.objects.create(post=post.pk, image=image)
        return post
但我需要使用一个序列化器来完成。
英文:
I'm getting a serializer error:
> "Upload a valid image. The file you uploaded was either not an image
> or a corrupted image"
When trying to serialize an uploaded image
My code:
Models:
class Post(models.Model):
    text = models.TextField(max_length=10000)
class Image(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    image = models.ImageField(blank=False, null=False, upload_to='test')
View:
class CreateNewPostAPIView(generics.CreateAPIView):
    serializer_class = serializers.NewPostSerializer
    def get_serializer_context(self):
        context = super().get_serializer_context()
        if self.request.data.get('image', None):
            context['image'] = self.request.data.pop('image', None)
        return context
Serializers:
class NewPostSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        post = Post.objects.create(**validated_data)
        image = self.context['image'][0] # working with only one image for now
        # image is of type <class'django.core.files.uploadedfile.InMemoryUploadedFile'>
        serializer = ImageSerializer(data={'post': post.pk, 'image': image})
        is_valid = serializer.is_valid()  # it's always False!
        return post
class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = ('post', 'image')
I already tried doing that without a serializer and it's working with this code:
class NewPostSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        post = Post.objects.create(**validated_data)
        image = self.context['image']
        Image.objects.create(post=post.pk, image=image)
        return post
But I need to do it using a serializer.
答案1
得分: 1
以下是翻译后的代码部分:
模型:
from django.db import models
# 在此创建您的模型
class Post(models.Model):
    text = models.TextField()
    def __str__(self):
        return self.text[:50]
class Image(models.Model):
    post = models.ForeignKey(
        Post, on_delete=models.CASCADE,
        related_name='images', editable=False
    )
    image = models.ImageField(upload_to='images/')
    def __str__(self):
        return self.image.name
    def delete(self, *args, **kwargs):
        self.image.delete()
        super().delete(*args, **kwargs)
序列化器:
from rest_framework import serializers
from .models import Post, Image
class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"
class PostWithImageSerializer(serializers.ModelSerializer):
    image = serializers.ImageField(write_only=True)
    # 这是为了GET请求或POST请求的响应
    # 我们也可以为这些情况使用单独的序列化器
    images = ImageSerializer(many=True, read_only=True)
    class Meta:
        model = Post
        fields = "__all__"
    
    def create(self, validated_data):
        image_data = validated_data.pop('image')
        post = Post.objects.create(**validated_data)
        Image.objects.create(post=post, image=image_data)
        return post
视图:
from rest_framework.response import Response
from rest_framework import status
from .serializers import *
# 在此创建您的视图
class AddPostWithAnImageView(APIView):
    serializer_class = PostWithImageSerializer
    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
出于测试目的,请使用 Postman,您可以轻松上传图片。请求表单应仅需要text和image字段。
英文:
The easy way for this would be like:
Models:
from django.db import models
# Create your models here.
class Post(models.Model):
    text = models.TextField()
    def __str__(self):
        return self.text[:50]
class Image(models.Model):
    post = models.ForeignKey(
        Post, on_delete=models.CASCADE,
        related_name='images', editable=False
    )
    image = models.ImageField(upload_to='images/')
    def __str__(self):
        return self.image.name
    def delete(self, *args, **kwargs):
        self.image.delete()
        super().delete(*args, **kwargs)
Serializer:
from rest_framework import serializers
from .models import Post, Image
class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = "__all__"
class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = "__all__"
class PostWithImageSerializer(serializers.ModelSerializer):
    image = serializers.ImageField(write_only=True)
    # This is for the GET request or the response of the POST request
    # We can also work with a separate serializer for such cases
    images = ImageSerializer(many=True, read_only=True)
    class Meta:
        model = Post
        fields = "__all__"
    
    def create(self, validated_data):
        image_data = validated_data.pop('image')
        post = Post.objects.create(**validated_data)
        Image.objects.create(post=post, image=image_data)
        return post
Views as:
from rest_framework.response import Response
from rest_framework import status
from .serializers import *
# Create your views here.
class AddPostWithAnImageView(APIView):
    serializer_class = PostWithImageSerializer
    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
For testing purposes, please use Postman where you can upload images with so much ease. The request form should only require the text and the image fields.
答案2
得分: 0
Thanks to @pKiran,提供了一个想法,现在我有一个可以保存一个或多个图像的工作代码。而且,它也非常简洁和可读。在这里,只用了很少的代码就完成了很多工作。Django REST 颇具迷惑性。
class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
    fields = ('image', 'thumb')
class NewPostSerializer(serializers.ModelSerializer):
    image = serializers.ListField(child=serializers.ImageField(), write_only=True, required=False)
    def create(self, validated_data):
        images = validated_data.pop('image', None)
        post = Post.objects.create(**validated_data)
        if images:
            img_models = [Image(post=post, image=image, thumb=make_thumb(image)) for image in images]
            Image.objects.bulk_create(img_models)
        return post
英文:
Thanks to @pKiran, who gave me an idea, now I have a working code for saving one or more images. It's, also, quite concise and readable. There's quite a lot going on here with so little of code. Django REST is quite confusing.
class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = ('image', 'thumb')
class NewPostSerializer(serializers.ModelSerializer):
    image = serializers.ListField(child=serializers.ImageField(), write_only=True, required=False)
    def create(self, validated_data):
        images = validated_data.pop('image', None)
        post = Post.objects.create(**validated_data)
        if images:
            img_models = [Image(post=post,
                          image=image, thumb=make_thumb(image))
                          for image in images]
            Image.objects.bulk_create(img_models)
        return post
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论