Search code examples
pythondjangodjango-rest-frameworkdjango-serializerdjango-uploads

Serialize multiple InMemoryUploadedFile using ListField : Django REST Framework


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."
        ]
    }
}

Solution

  • 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]: <QueryDict: {'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]: <QueryDict: {'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]: <QueryDict: {'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]: <QueryDict: {'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)