Uploading Files with Django Ninja

We've covered how to create a basic CRUD API with django-ninja in previous posts: Firstly, our Introduction to Django-Ninja. And subsequently, our post on Building a full CRUD API using Django-Ninja.

Let's go one step further, and find out how to upload files using django-ninja.

The associated video for this post can be found below.


Objectives

  • Learn how to upload files to a django-ninja API endpoint

Setup

This post will extend the setup found in this tutorial.

We are going to start by creating an HTML template which will contain a simple HTML form, with a single input for file-selection.

Navigate to the tracks directory, and create a new directory within there called templates. Then, in the templates directory, create a file called index.html

We will now create a simple Django view to render this template. Add the following code to views.py.

from django.shortcuts import render

def index(request):
    return render(request, 'index.html', {})

And now, create a URL endpoint to point to this new view. Add this code to your djninja/urls.py file.

from django.contrib import admin
from django.urls import path
from tracks.api import api
from tracks.views import index

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

This will allow you to view the index.html template at http://localhost:8000/index.

Now, we're ready to create a new django-ninja endpoint in api.py. Add the following lines to this file.

from ninja import File
from ninja.files import UploadedFile

@api.post("/upload", url_name='upload')
def upload(request, uploaded_file: UploadedFile = File(...)):
    # UploadedFile is an alias to Django's UploadFile
    data = uploaded_file.read().decode()
    return {'name': uploaded_file.name, 'len': len(data), 'data': data}

django-ninja has a few objects that make our lives a bit easier when dealing with files - the File object, and the UploadedFile object. The UploadedFile object is an alias to Django's UploadFile - the main class in Django that deals with file uploads, and which is subclassed by TemporaryUploadedFile and InMemoryUploadedFile.

We define our API route decorator on line 4, and notably, we give this route a url_name="upload". This url_name parameter corresponds to the name parameter to the Django path() method, allowing you to give your endpoints names that can be referenced elsewhere in the codebase (this is useful for avoiding hard-coding URLs). This will allow us to refer to the upload endpoint when setting the action property of our HTML form.

On line 5, we take a second parameter to our API function specifying the UploadedFile, of type File. We can then read the file data to a bytes object using file.read(), and can chain .decode() to decode the bytes to a Python string.

Finally, we return a dictionary with the file's name, its length, and the data itself.

With this in place, we can now create a form in our index.html template. Add the following HTML code to that template.

<form method="POST" action="{% url 'api-1.0.0:upload' %}" enctype="multipart/form-data"> 
    <input type="file" name="file" /> 
    <button type="submit">Submit</button> 
</form> 

If the Django development server is running, you should see this form at: http://localhost:8000/index. Notice that we refer to the upload URL in the action attribute of the HTML form element - this ensures our form's POST request goes to the above upload endpoint.

As a test, upload the requirements.txt file, and you should see a response such as the following.

{
  "name": "requirements.txt",
  "data": "Django==3.2.3\r\ndjango-ninja==0.13.2"
}

This displays the filename and the contents of the uploaded file, as specified in our API view.

You can choose to save the file to your server's filesystem, or perform any tasks you wish on the data in the file.

Summary

That's it! It's very easy to upload files with django-ninja.

If you enjoyed this post, please subscribe to our YouTube channel and follow us on Twitter to keep up with our new content!

Please also consider buying us a coffee, to encourage us to create more posts and videos!

;