In this tutorial I will guide you through the steps to implement an AJAX multiple file upload with Django using jQuery. For this tutorial we will be using a specific plug-in called jQuery File Upload, which takes care of the server communication using AJAX and also the compatibility with different browsers.
The plug-in is great, but it have so many features that sometimes it can become challenging for some to get started. You will notice that some of the examples are a little bit redundant, repeating code and so on. That’s on purpose, so to avoid code abstraction and the examples become more clear.
In the end of this post you will also find the link to download all the code used in this tutorial.
Basic Configuration
Before you move forward, if you are not familiar at all with file upload with Django, it is a good a idea to check this post I published while ago: How to Upload Files With Django. It will give you an overview of the basics and some caveats.
To work with file upload you will need to set the MEDIA_URL
and MEDIA_ROOT
.
settings.py
And to test the uploads in the development environment, add this to the bottom of the root urlconf:
urls.py
Now the rest are static assets, mostly JavaScript assets. Here’s what we are going to use:
- Bootstrap 3.3.7 Not required. Just so our example looks better.
- jQuery 3.1.1 Base dependency of the plug-in.
- jQuery File Upload 9.14.1 That’s the stuff.
Now you will see that the jQuery File Upload comes with several script files, they all have a purpose and you will only need some of them for certain features.
Here is how my base template looks like:
base.html
Bootstrap and jQuery in the base template, and the jQuery File Upload plug-in will be added using the
{% block javascript %}
. We will add them as we need.
Working Example
We will be working in a app called photos. Consider the model below for the rest of this tutorial:
models.py
And the form, as simple as:
forms.py
PS: Perhaps using a ImageField
instead of FileField
would be more suitable for the use case, but to avoid the
hustle of installing Pillow locally, let’s just use FileField
. So we have an easier to run example.
Basic File Upload
First we need a route:
urls.py
A view to process the upload, nothing special:
views.py
We are using a basic Class-Based View, defining two request processing for the GET and POST methods. Essentially when we access the page where we handle the upload, we are going to show the user a list of uploaded photos.
For the POST, it is where the upload handling happens, using Django’s Model Forms. When we call form.save()
, Django
will create a Photo
instance and save the file in the file system.
We will follow the same strategy for the next examples as well.
Now where the magic happens: the client side. First the required jQuery File Upload scripts:
photos/basic_upload/index.html
- jquery.ui.widget.js It’s a dependency for the plug-in
- jquery.iframe-transport.js The Iframe Transport is required for browsers without support for XHR file uploads
- jquery.fileupload.js The basic File Upload plug-in
And finally the script basic-upload.js is where we will implement our photo upload.
Now let’s see the rest of our template and explore the meat of the page:
photos/basic_upload/index.html
First, block 1 of the snippet, is the button to start the workflow. We will hook into the css class .js-upload-photos to open the file explorer window.
Block 2 is the most important part of the page. It’s the the file input that will be used to load the jQuery File Upload component. A few things to note:
- The name of the input must match with the Model Form Field. That is, if the
FileField
is called document, the input must be namedname="document"
instead. Otherwise the processing of the file will fail. - The
multiple
attribute will enable multiple file selection in the file explorer window. - The
data-url
attribute must point to the route/view where the file form will be processed. - The
data-form-data
attribute should be defined exactly this way! Don’t try to swap the"
and'
of the HTML. It won’t work. This line is important so to instruct the plug-in to send the file along with the csrf middleware token.
Finally, block 3 is just a regular table displaying the photos.
Now let’s explore the page’s script, which is responsible for putting every piece of this puzzle together:
photos/js/basic-upload.js
Remember how we returned the data in the View class?
This JsonResponse
will end up in the data parameter, passed to the anonymous function hooked to the done
event of the File Upload component.
See what we are doing here? When we are accessing data.result.name
, we are accessing the name we returned in the
JsonResponse
. So, let’s say, if we returned:
We would be able to catch it inside the done function, this way:
But, what we are actually doing there is putting some pieces of HTML together and prepending it to the table body.
Let’s look at some screen shots to see how it looks like:
Then when the user clicks in the Upload photos button:
The user selects as may files as he wants and hit the Open button:
And all the files are uploaded to the server and added to the table, with a valid link! This is a minimum example. When you grab the code you will see it is really easy to reproduce.
It’s great already! Works on most major web browsers (IE 6.0+ for example!). And it is just a matter of what data to return, and what to do with it.
Displaying the Progress
An improvement we can make, is to add a progress bar, to not let the users hanging without knowing what is going on.
In our template, add the following snippet. It’s a Bootstrap modal:
photos/basic_upload/index.html
Let’s improve the JavaScript part so to calculate the progress:
photos/js/basic-upload.js
The sequentialUploads attribute will instruct the component to send one file at a time. What we are doing here basically is showing the loading modal when the upload starts, closing it when it finalizes, meanwhile we update the percentage in the progress bar. The rest is the same from the other example.
Here is how it will look like:
Drag and Drop Upload
Something great about this plug-in, by default you can drop files anywhere in the page to start the upload process! This mean the previous examples will simply work.
What we can do to help the users discover the functionality is just adding this simple HTML, for example:
photos/basic_upload/index.html
And the result will be something like this:
Conclusions
I wanted to give more tips and examples about the jQuery File Upload usage. But the post was already getting big. I will continue this post next week. So, let me know what you want to see next!
Anyway, the examples shown in this tutorial should cover the Django part. The rest is a matter of configuration and playing with the plug-in. For more details about its API and features, refer to the official documentation: jQuery File Upload Docs
The code used in this tutorial is available on GitHub: github.com/sibtc/multiple-file-upload, so you can try it locally. It’s very straightforward to get it running.