1

Note: Please read the whole question before marking it as duplicate.

Django has changed ALOT Since a couple of years.

I have already tried all the solutions and read Django Rest Framework Documentation on:

None of the ways work. I also tried uploading using simple Django but that didnt seem to work Either.

The Error I am getting is that request.FILES['file'] is always an empty {}.

Write now my code looks like this

    parser_classes = [MultiPartParser]

    def put(self, request, format=None):
        file_obj1 = request.data['file']
        data = json.loads(request.POST['form'])
        print(file_obj1)

        return Response(status=status.HTTP_201_CREATED)

Like i said I always tried Using FileUploadParser. Same code just replaced MultiPartParser with FileUploadParser. I am using Postman to send an excel file and then reading it. Anyone who has recently upload an excel file in Django Rest Framework can guide me but make sure your code works becuase I have already spent a day and still can't find the solution so far.

Edit: Curl Request from Postman would be: curl --location 'http://localhost:8000/amm-api/upload/upload_workbook/' \ --form '=@"/Users/razan/Projects/HTS/ags-carmax/Robbie_Lisa-Branch.xlsx"'

This is how it looks in Postman enter image description here

4
  • did you use form data? Commented Apr 14, 2023 at 18:10
  • I am using Postman to send an excel file and then reading it. Can you export the postman request as cURL and include it in the question? There are many ways to send a file to a server, and the way you access the file depends on how the file is being sent. Commented Apr 14, 2023 at 18:14
  • I also added in the Update but Its this: curl --location 'http://localhost:8000/amm-api/upload/upload_workbook/' \ --form '=@"/Users/razan/Projects/HTS/ags-carmax/Robbie_Lisa-Branch.xlsx"' Commented Apr 14, 2023 at 18:37
  • @KayesFahim Yes I have. Added the Postman screenshot in the main question for your reference Commented Apr 14, 2023 at 18:38

2 Answers 2

3

After spending 2 days over it I have finally found a way by hit and try. Please try it and let me know. As of today it works on Django 3.8.2

views.py

from rest_framework.parsers import FileUploadParser
import pandas as pd
from rest_framework import viewsets, permissions
from rest_framework.views import APIView
from rest_framework.response import Response


class FileUploadView(APIView):
parser_classes = [FileUploadParser]
permission_classes = [permissions.AllowAny]

def put(self, request, filename, format=None):
    try:
        file_obj = request.data['file']
        file_content = file_obj.read()
        df = pd.read_excel(file_content, engine='openpyxl')
        return Response({'status': 'success', 'message': 'File uploaded.'})
    except Exception as e:
        error_message = str(e)
        return Response({'status': 'error', 'message': error_message})

urls.py

from django.urls import path, include, re_path
from . import views
from amm_core.views import FileUploadView

urlpatterns = [
    re_path(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view())
]

settings.py (Make sure to include the certain permissions.)

'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.MultiPartParser',
        'rest_framework.parsers.FormParser',
    ],
 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'

Then from Postman hit the API with and excel selected. http://localhost:8000/<app-name><optional-sub-appname>/upload/<filename>.xlsx Postman image

Sign up to request clarification or add additional context in comments.

Comments

2

I've been banging my head against the wall trying to get the original scenario (APIView with POST handler using MultiPartParser) to work, and I finally got it done.

I'm going to summarize everything I found as succinctly as possible, then explain:

  • To handle properly a mixed HTML form which includes data AND files, you need to use 2 parsers at the same time: MultiPartParser and FormParser.

  • request.FILES['file'] will come empty if your form doesn't include enctype="multipart/form-data"

  • If you are using Postman (like you were, and like I was), and you are NOT using FileUploadParser and PUT method, you NEED to add a Key to the file being sent in the form-data, and this Key must be the same you are trying to read from the request.FILES['file'] dictionary (so the key in Postman should be 'file').

This will make it work.

Now, I'll go in some detail for those who still need it:

  • In the initial solution posted by @nabiharaza he switches from MultiPartParser to FileUploadParser to make it work. This is fine if you don't intend to send additional form stuff with the file. But if you are sending mixed form data, you must use MultiPartParser and FormParser, like this:
class FileUploadAPI(APIView):

    parser_classes = [MultiPartParser, FormParser]

    def post(self, request, filename, format=None):

    # etc etc
  • When you use MultiPartParser, you are going to get a dictionary (MultiValueDict) on request.FILES, which you can access by key. But Django Documentation warns us that if we don't have the enctype set to multipart/form-data the dictionary will come empty. That's the main issue here.

Note that request.FILES will only contain data if the request method was POST, at least one file field was actually posted, and the that posted the request has the attribute enctype="multipart/form-data". Otherwise, request.FILES will be empty.

(from the Django Docs link above).

In HTML, it would look like this:

<form method="post" action="{% url catalog_create_ajax_upload %}" enctype="multipart/form-data" id="create-form">

But there is one more thing (see what I did here? :D) for those of us using Postman:

  • On Postman, when you use the POST method, and select a file in the form-data section, it automatically creates a form with the enctype="multipart/form-data" attribute, so you don't need to add it manually by yourself. But you still need to set the KEY corresponding to the file (this is not mandatory for the PUT method handled by FileUploadParser because of how it works). If you check the original screenshot from @nabiharaza, you will notice that there isn't a key associated to the file Robbie_Lisa-Branch.xlsx.

So, even if everything else is correct, the request.FILES dictionary will come empty, and the request.FILES['file'] access will throw an Exception.

If you add the file key to Postman, it will work like a charm :)

form-data with proper file key

Of course, if you have several files or you want to use something_else instead of file you can, but you need to match it in your dictionary access to request.FILES['something_else'].

I hope this is helpful for anyone trying to get this working and still struggling. I found bits and pieces of this info scattered all around the web (StackOverflow included) but not together on a single place.

Have fun!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.