Django Rest Framework File Upload API

Posted by Vivek Shukla on Jun 21, 2023 under Django Rest Framework

Uploading file via API is a very common requirement. In this guide we will learn to build Django Rest Framework File Upload API.

Setup

  • Django: 4.2

  • Django Rest Framework: 3.14.0

  • Project Name: fileupload

  • App Name: api

Add your app in INSTALLED_APPS

In fileupload/settings.py

INSTALLED_APPS = [
    .....
    'rest_framework',
    'api', # your django app
]

Update MEDIA path in settings.py

We need to set the path where we want the uploaded files to be stored.

In settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

Update project’s urls.py file

For Django to be able to serve media files we need to enable this using static helper function that Django provides. This only works in DEBUG mode and only if folder location is local. source

In fileupload/urls.py

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls', namespace='api')),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Model

Let’s create a Model where we store the file path (as file) and file upload time (as uploaded_on). So the uploaded file will be stored in media folder and file field will contain it’s path.

In your api/models.py

class UploadedFile(models.Model):
    file = models.FileField()
    uploaded_on = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.uploaded_on.date()

Run the migration

python manage.py makemigrations
python manage.py migrate

Serializer

Creating a ModelSerializer for our Model UploadedFile. ModelSerializer creates serializer fields based on Model passed and comes with buil-in save method that saves passed data into database.

In your api/serializers.py

from rest_framework import serializers
from .models import UploadedFile

class FileUploadSerializer(serializers.ModelSerializer):
    class Meta:
        model = UploadedFile
        fields = ('file', 'uploaded_on',)

View

Now we create the view class FileUploadAPIView (sub-class of APIView) to save the uploaded file. Few things to note:

  • Generally the JSONParser is set as the default parser in DRF so that we can send the request in JSON format. However it is not advised/convenient to upload file in JSON.

  • form-data is the best way to upload the file, to enable this we need to pass MultiPartParser and FormParser as our parser_classes in our view.

  • Only MultiPartParser can be passed to enable file uploading but as per DRF API Guide on MultiPartParser, we should use both parser classes to fully support HTML form data.

In api/views.py

from rest_framework import status
from rest_framework.parsers import FormParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView

from .serializers import FileUploadSerializer

class FileUploadAPIView(APIView):
    parser_classes = (MultiPartParser, FormParser)
    serializer_class = FileUploadSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid():
            # you can access the file like this from serializer
            # uploaded_file = serializer.validated_data["file"]
            serializer.save()
            return Response(
                serializer.data,
                status=status.HTTP_201_CREATED
            )

        return Response(
            serializer.errors,
            status=status.HTTP_400_BAD_REQUEST
        )

Urls

Let’s connect our view in urls to be able to receive requests on it.

In api/urls.py

from django.urls import path
from .views import FileUploadAPIView, ImageUploadAPIView

app_name = 'api'

urlpatterns = [
    path('upload-file/', FileUploadAPIView.as_view(), name='upload-file'),
]

Testing API Endpoint

Start the server

python manage.py runserver

Postman

  • Method: POST (as per our view)

  • URL: http://localhost:8000/api/upload-file/

  • Body Type: form-data

  • Values to be passed

    • file as KEY (change the type to File in dropdown), Browse and select a file for VALUE

cURL

curl --location --request POST 'http://localhost:8000/api/upload-file/' --form 'file=@"/path/to/yourfile.pdf"'

NodeJS - Axios

var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('file', fs.createReadStream('/path/to/yourfile.pdf'));

var config = {
  method: 'post',
  url: 'http://localhost:8000/api/upload-file/',
  headers: {
    ...data.getHeaders(),
  },
  data: data,
};

axios(config)
  .then(function (response) {
    console.log(JSON.stringify(response.data));
  })
  .catch(function (error) {
    console.log(error);
  });

Checkout this guide to learn how to store static and media files in AWS S3

Built with  Svelte Starter Kit