2

How can I serialize multiple InMemoryUploadedFile using serializers.ListField() ??


code snippet

#views.py
@api_view(['POST', 'GET'])
def create_post(request):
    if request.method == 'POST':
        altered_request_data = request.data.copy()
        in_memory_upload_files_list = [value for value in request.FILES.values()]
        altered_request_data['files'] = in_memory_upload_files_list
        serializer = PostSerializer(data=altered_request_data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(data=serializer.data, status=201)
    else:
        qs = Post.objects.all()
        serializer = PostSerializer(qs, many=True)
        return Response(serializer.data)

#serilizers.py
class PostSerializer(serializers.ModelSerializer):
    files = serializers.ListField(child=serializers.FileField(), write_only=True)

    class Meta:
        fields = '__all__'
        model = Post

current response

{
    "files": {
        "0": [
            "The submitted data was not a file. Check the encoding type on the form."
        ]
    }
}
JPG
  • 82,442
  • 19
  • 127
  • 206
  • i think you does not need it, because you call `save` just process the files in the save and all be fine. – Brown Bear Apr 25 '19 at 08:54
  • the given response is the validation error and it raised before calling the `save()` method – JPG Apr 25 '19 at 09:00

2 Answers2

1

The issue was lies in this line,

altered_request_data['files'] = in_memory_upload_files_list

here the altered_request_data is a QueryDict object, so if we assign anything to it will call the __setitem__() method

In [6]: from django.http import QueryDict                                                                                                                                                                          

In [7]: qd = QueryDict('a=1&a=2&c=3',mutable=True)                                                                                                                                                                 

In [8]: qd                                                                                                                                                                                                         
Out[8]: &ltQueryDict: {'a': ['1', '2'], 'c': ['3']}>

In [9]: my_list = [i for i in range(10)]                                                                                                                                                                           

In [10]: my_list                                                                                                                                                                                                   
Out[10]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [11]: qd['foo']=my_list                                                                                                                                                                                         

In [12]: qd                                                                                                                                                                                                        
Out[12]: &ltQueryDict: {'a': ['1', '2'], 'c': ['3'], 'foo': [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]}>

See that? qd['foo'] become a list of list. That is, anything assigning to a QueryDict will be stored inside a list object.

So, what's the solution??

QueryDict class has a method QueryDict.setlist will do the job

In [15]: qd__new                                                                                                                                                                                                   
Out[15]: 

In [16]: qd__new = QueryDict('a=1&a=2&c=3',mutable=True)                                                                                                                                                           

In [17]: qd__new                                                                                                                                                                                                   
Out[17]: &ltQueryDict: {'a': ['1', '2'], 'c': ['3']}>

In [18]: my_list                                                                                                                                                                                                   
Out[18]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [19]: qd__new.setlist('foo_new',my_list)                                                                                                                                                                        

In [20]: qd__new                                                                                                                                                                                                   
Out[20]: &ltQueryDict: {'a': ['1', '2'], 'c': ['3'], 'foo_new': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}>

code snippet

views.py

@api_view(['POST', 'GET'])
def create_post(request):
    if request.method == 'POST':
        altered_request_data = request.data.copy()
        in_memory_upload_files_list = [value for value in request.FILES.dict().values()]

        altered_request_data.setlist('files', in_memory_upload_files_list)

        serializer = PostSerializer(data=altered_request_data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(data=serializer.data, status=201)
    else:
        qs = Post.objects.all()
        serializer = PostSerializer(qs, many=True)
        return Response(serializer.data)
Community
  • 1
  • 1
JPG
  • 82,442
  • 19
  • 127
  • 206
0

You should use getlist method of FILES that will give you List of Files.

in_memory_upload_files_list = request.FILES.getlist('<file field name>')
altered_request_data['files'] = in_memory_upload_files_list
serializer = PostSerializer(data=altered_request_data)

Here <file field name> will be name file field on which you upload file.

https://docs.djangoproject.com/en/2.2/ref/request-response/#django.http.QueryDict.getlist

Neeraj Kumar
  • 3,851
  • 2
  • 19
  • 41
  • I'm uploading the files as this - [screenshot](https://i.stack.imgur.com/DFn6F.png) and I am not interested to specify the names in `getlist()` method, because I'm uploading multiple files – JPG Apr 25 '19 at 09:24
  • you are using key "file" just put "file" replace in In you snapshot you mentioned file_1, file_2 means postman uploading images based on "file" field name. – Neeraj Kumar Apr 25 '19 at 09:27
  • Without key you can't get anything for FILES, POST and GET. you will always get dictionary in these method. so that always need keys to access it – Neeraj Kumar Apr 25 '19 at 09:28
  • 1
    What if I want to upload **`n`** number of files? and the DRF side is not aware of which `key` (such as `file_1`,`file_2`,`file_3` ..etc)is used to send those `n` files. That's why I used the ***`.values()`*** method – JPG Apr 25 '19 at 09:33
  • https://stackoverflow.com/questions/39645410/how-to-upload-multiple-files-in-django-rest-framework try this way – Neeraj Kumar Apr 25 '19 at 09:37
  • that too didn't help me. Anyway I found a solution :) – JPG Apr 25 '19 at 10:17
  • @JPG Please share the solution :( – Shahriar Rahman Zahin Jun 10 '20 at 19:42