Marin
Marin

Reputation: 1121

Django Rest Framework upload multiply images at once with serializer

I had some research on web for this problem on DRF. When one image is in post DRF works great. But when 3 or 5 images are in one post there is problem that Django save only first image from POST query and others are not saved in database. The question is how to properly handle multiply images upload in one post.

Description

Django version

Rest framework version

Python version

Here is my code what I was trying to do:

models

class UserModel(AbstractEmailUser):
    first_name = models.CharField(max_length=30, db_index=True)
    last_name = models.CharField(max_length=50, db_index=True)
    details = JSONField(null=True, blank=True, db_index=True)
    avatar = models.ImageField(blank=True, null=True, upload_to='avatar')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.email)

class UserImages(models.Model):
    image = models.ImageField(upload_to='images', db_index=True)
    user = models.ForeignKey('UserModel',related_name='user')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return str(self.user)

Here in models I have related field from User to UserImages. For purpose on POST query from postman I manual add ID from user to handle FK.

serializer

class SerializerUserImages(serializers.ModelSerializer):
    
    class Meta:
        model = UserImages

    def create(self, validated_data):
        imgs = UserImages.objects.create(**validated_data)
        return imgs

Maybe here I must do some for loop to accomplish save method for multiply images.

views

class UploadImages(APIView):
    authentication_classes = (JSONWebTokenAuthentication,)

    @parser_classes((FormParser, MultiPartParser, FileUploadParser))
    def post(self, request, format=None):

        serializer = SerializerUserImages(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(data={"msg": serializer.data}, status=status.HTTP_200_OK)
        else:
            return Response(data={"msg": serializer.errors}, status=status.HTTP_406_NOT_ACCEPTABLE)

All parsers are included for handling this post query.

This is output from request.data = <QueryDict: {'image': [<InMemoryUploadedFile: top_20_cro.png (image/png)>, <InMemoryUploadedFile: images.jpg (image/jpeg)>], 'user': ['2', '2']}>

This is copy of request POST from POSTMAN:

POST /api/images/ HTTP/1.1
Host: 127.0.0.1:8000
Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJvcmlnX2lhdCI6MTQ3MjIwMTcwMSwiZXhwIjoxNDcyMjMxNzAxLCJ1c2VyX2lkIjoyLCJlbWFpbCI6Im1hcmluLmJyZWthbG9AZ21haWwuY29tIiwidXNlcm5hbWUiOiJtYXJpbi5icmVrYWxvQGdtYWlsLmNvbSJ9.Fi3kmXJ44E_qhHHioniQ-cqri-u2ELU-XmpE_1oJ4Fk
Cache-Control: no-cache
Postman-Token: 1e53ac06-ed37-ff6e-2dc5-117976b86a5e
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="image"; filename=""
Content-Type: 


------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="user"

2
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="filename"

image
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="user"

2
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="filename"

image
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="image"; filename=""
Content-Type: 


------WebKitFormBoundary7MA4YWxkTrZu0gW--

How can this be handled without some great magic, is there some easy logic that I missed up.

Upvotes: 1

Views: 1908

Answers (3)

Marin
Marin

Reputation: 1121

Ok here is my some logic about this problem, so I hope someone will use this code (hack) for this purpose:

Serializer change

class SerializerTest(serializers.Serializer):
    image = serializers.ListField(child=serializers.ImageField(required=True))


    def create(self, validated_data):

        for attr, value in validated_data.items():
            if attr == 'image':
                for x in value:
                    c = UserImages.objects.create(image=x, user_id=UserModel.objects.get(id=self.context['request'].user.id).id)
                    c.save()
        return validated_data

I have updated my serialzer for just one field img. And use for loop to extract images from POST. As you can see i call query to store images to database on FK user. This work's very good.

Views

class UploadImages(APIView):
    authentication_classes = (JSONWebTokenAuthentication,)

    @parser_classes((FormParser, MultiPartParser, FileUploadParser))
    def post(self, request, format=None):



        serializer = SerializerTest(data=request.data, context={'request':request})

        if serializer.is_valid():
            serializer.save()
            if len(serializer.data['image']) > 0:
                que = UserImages.objects.filter(user=request.user.id)
                ser = SerializerUserImages(que, many=True)
                return Response(data={"msg": ser.data}, status=status.HTTP_200_OK)
            else:
                return Response(data={"msg": 'Provide images!'}, status=status.HTTP_406_NOT_ACCEPTABLE)
        else:
            return Response(data={"msg": serializer.errors}, status=status.HTTP_406_NOT_ACCEPTABLE)

Upvotes: 1

Swakeert Jain
Swakeert Jain

Reputation: 776

Simply add

many=True

with the serializer initializer. For eg.:

serializer = SerializerUserImages(data=request.data, many=True)

This should do the trick.

Also have a look at ListSerializers

Upvotes: 0

Sassan
Sassan

Reputation: 2339

Try setting filenames for your images, I mean replace filename="" with something like filename="image-1", filename="image-2", etc. Files should have different filenames or they override each other.

Upvotes: 0

Related Questions