Django REST 在序列化上传的图片时出现错误。

huangapple go评论58阅读模式
英文:

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=&#39;test&#39;)

View:

class CreateNewPostAPIView(generics.CreateAPIView):
    serializer_class = serializers.NewPostSerializer

    def get_serializer_context(self):
        context = super().get_serializer_context()
        if self.request.data.get(&#39;image&#39;, None):
            context[&#39;image&#39;] = self.request.data.pop(&#39;image&#39;, None)
        return context

Serializers:

class NewPostSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        post = Post.objects.create(**validated_data)
        image = self.context[&#39;image&#39;][0] # working with only one image for now
        # image is of type &lt;class&#39;django.core.files.uploadedfile.InMemoryUploadedFile&#39;&gt;

        serializer = ImageSerializer(data={&#39;post&#39;: post.pk, &#39;image&#39;: image})
        is_valid = serializer.is_valid()  # it&#39;s always False!
        return post

class ImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Image
        fields = (&#39;post&#39;, &#39;image&#39;)

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[&#39;image&#39;]
        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,您可以轻松上传图片。请求表单应仅需要textimage字段。

英文:

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=&#39;images&#39;, editable=False
    )
    image = models.ImageField(upload_to=&#39;images/&#39;)

    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 = &quot;__all__&quot;

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = &quot;__all__&quot;

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 = &quot;__all__&quot;
    
    def create(self, validated_data):
        image_data = validated_data.pop(&#39;image&#39;)
        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 = (&#39;image&#39;, &#39;thumb&#39;)

class NewPostSerializer(serializers.ModelSerializer):
    image = serializers.ListField(child=serializers.ImageField(), write_only=True, required=False)

    def create(self, validated_data):
        images = validated_data.pop(&#39;image&#39;, 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

huangapple
  • 本文由 发表于 2023年2月18日 20:50:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75493459.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定