Как правильно сериализовать объект, модель которого имеет внешний ключ, который также принимает ImageField? - PullRequest
0 голосов
/ 19 октября 2018
# models.py

class Post(models.Model):
    content = models.TextField(blank=True, default='')
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

class PostImage(models.Model):
    image = models.ImageField(upload_to=unique_upload)
    post = models.ForeignKey(
        Post, related_name='images', on_delete=models.CASCADE)

Это моя модель, настроенная для базового сценария, когда пользователь может вводить контент или загружать изображения в качестве своих сообщений.

Я хочу связать свою логику для обработки создания сообщения с помощью content или images или оба.

Я впервые начал играть с GenericViewSet и CreateViewSet, но изображения никогда не передавались моему сериализатору.

# views.py

class CreatePostViewSet(generics.CreateAPIView /* viewsets.GenericViewSet */):
    permission_classes = (IsAuthenticated,)
    queryset = Post.objects.order_by('id')
    serializer_class = CreatePostSerializer

    def create(self, request, *args, **kwargs):
        data = {}
        print(request.data)
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        serializer.save(created_by=request.user)

        # post = serializer.instance
        # print(post)
        # for im in post.images.all():
        #     im.save(post=post)
        # print(post.images.all())

        return Response(data,
                        status=status.HTTP_201_CREATED,
                        headers=self.get_success_headers(serializer.data))

# serializers.py

class PostImageSerializer(serializers.ModelSerializer):

    class Meta:
        model = PostImage
        fields = ('id', 'url', 'image', 'post',)
        read_only_fields = ('post',)
        depth = 1

class CreatePostSerializer(serializers.ModelSerializer):
    images = PostImageSerializer(many=True, required=False)

    class Meta:
        model = Post
        fields = ('id', 'url', 'content', 'images',)
        read_only_fields = ('created_by',)
        depth = 1

    def create(self, validated_data):
        # validated_data['images'] is always []
        print(validated_data)
        raise

imagesвсегда равен [], когда я передаю его сериализатору, но он существует в request.data['images'] как [<TemporaryUploadedFile: 1 - 5H5hHgY.png (image/png)>, ...

Я надеялся использовать ModelSerializer для автоматического разрешения ImageField.

# CreatePostSerializer serializers breaks down to

CreatePostSerializer():
    id = UUIDField(read_only=True)
    url = HyperlinkedIdentityField(view_name='post-detail')
    content = CharField(allow_blank=True, required=False, style={'base_template': 'textarea.html'})
    images = PostImageSerializer(many=True, required=False):
        id = UUIDField(read_only=True)
        url = HyperlinkedIdentityField(view_name='postimage-detail')
        image = ImageField(max_length=100)
        post = NestedSerializer(read_only=True):

            id = UUIDField(read_only=True)
            content = CharField(allow_blank=True, required=False, style={'base_template': 'textarea.html'})
            created_by = PrimaryKeyRelatedField(queryset=User.objects.all())

Ответы [ 2 ]

0 голосов
/ 19 октября 2018

Итак, я смог заставить его работать с предложением @ ARJMP.

# views.py

class CreatePostViewSet(generics.CreateAPIView):
    # authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)
    queryset = Post.objects.order_by('id')
    serializer_class = CreatePostSerializer

    def create(self, request, *args, **kwargs):
        data = {}
        print(request.data)

        images = [{'image': i} for i in request.data.pop('images', [])]
        serializer = self.get_serializer(
            data={'content': request.data['content'], 'images': images})
        serializer.is_valid(raise_exception=True)

        post = serializer.save(created_by=request.user)
        # self.perform_create(serializer)
        data['post'] = serializer.data

        return Response(data,
                        status=status.HTTP_201_CREATED,
                        headers=self.get_success_headers(serializer.data))

# serializers.py

class CreatePostSerializer(serializers.ModelSerializer):
    images = PostImageSerializer(many=True, required=False)

    class Meta:
        model = Post
        fields = ('id', 'content', 'images',
                  'is_private', 'created_by',)
        read_only_fields = ('view_count', 'created',)
        depth = 1

    def create(self, validated_data):
        images = validated_data.pop('images', [])
        p = Post.objects.create(**validated_data)
        for im in images:
            pi = PostImage.objects.create(image=im['image'], post=p)
        return p

Мне кажется, что это кажется довольно запутанным, чтобы заставить его работать.Много манипулирую этим сам.Я действительно надеялся использовать больше "волшебного" материала, который делается с ModelSerializer и CreateAPIView.

Есть ли лучшие подходы для этого?

0 голосов
/ 19 октября 2018

Он думает, что request.data['images'] нужно будет немного изменить, потому что ваш PostImageSerializer будет ожидать объект, содержащий клавишу "image", в то время как вы передаете список TemporaryUploadedFile.

Given request.data['images'] Вы можете сделать что-то вроде следующего в своем представлении, прежде чем передавать данные в сериализатор:

images_list: List[TemporaryUploadedFile] = request.data.pop("images") 
images = []
for image in images_list:
    images.append({
        "image": image,
    })
request.data["images"] = images

Итак, мы преобразуем ваш список TemporaryUploadedFiles в список объектов с помощью ключа изображения.

: edit: То есть вы не хотите преобразовывать данные в представлении, чтобы они были совместимы с сериализатором?Затем вы можете изменить сериализатор, чтобы он был совместим с данными, это включает в себя настройку методов create и update, сейчас я покажу вам, как переопределить метод create.

class CreatePostSerializer(serializers.ModelSerializer):
    images = serializers.ImageField(many=True)

    class Meta:
        model = Post
        fields = ('id', 'url', 'content', 'images',)
        read_only_fields = ('created_by',)
        depth = 1

    def create(self, validated_data):
        images = validated_data.pop("images")
        post = super().create(validated_data)
        for image in images:
            serializer = PostImageSerializer(data={"image": image, "post": post.pk}, context=self.context)
            serializer.is_valid()
            serializer.save()
        return post

То есть вы не хотите переопределять данные в запросе и не хотите настраивать метод создания сериализаторов?Измените, как сериализатор преобразует ваши исходные данные в проверенные данные с помощью метода validate (я думаю, это работает для вложенных сериализаторов, но его не тестировали):

class CreatePostSerializer(serializers.ModelSerializer):
    images = PostImageSerializer(many=True, required=False)

    class Meta:
        model = Post
        fields = ('id', 'url', 'content', 'images',)
        read_only_fields = ('created_by',)
        depth = 1

    def validate(self, attrs):
        images_list = attrs.pop("images") 
        images = []
        for image in images_list:
            images.append({
                "image": image,
            })
        attrs["images"] = images
        return attrs
...