The 2019 way of posting FILES to Django server using jQuery.

04 June 2019


Writing a script to uploading files have always been my most daunting task with Django 2.0 and Python 3. There's a few online resources, but they mostly use the autogenerated .forms.py feature. I don't like that. I feel it's 2019, things should be more modern and dynamic by now.

This tutorial is not very newb friendly and requires some prior Python/Django and Javascript + jQuery knowledge... Copy and pasting won't work. It will work if you implement it correctly. You can easy customise it to your needs.

But after battling for a good 10 hours, I finally found a solution that works perfectly fine. No forms.

First things first - this method will upload your files to the /media folder of your Django project. So for that, add the following to your Settings.py file.

#In settings.py
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o755
FILE_UPLOAD_PERMISSIONS = 0o644
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

This will set your permissions as well as save your media file.

Now it's time for the HTML.

In my case, I used the Bulma css framework. They have a pretty cool looking upload button. I'm gonna go ahead and use that.

<div class="file is-boxed">
        <label class="file-label">
          <input id="profilePicUpload" class="file-input" type="file" >
          <span class="file-cta">
            <span class="file-icon">
              <i class="fas fa-upload"></i>
            </span>
            <span class="file-label">
              Choose a profile picture
            </span>
          </span>
        </label>
      </div>

Also, I know I'm talking about 2019 and doing things progressively, but still sometimes a project doesn't require React, Vue or Angular - so for this, I'll be using jQuery.

Now open your javascript and add the following.

$('#profilePicUpload').on('change', function () {
    var file = this.files[0];
    var url = '/profilepicupload';

    var data = new FormData();
    data.append('file', file);

    $.ajax({
        type: "POST",
        enctype: 'multipart/form-data',
        url: url,
        data: data,
        processData: false,
        contentType: false,
        cache: false,
        timeout: 600000,
        success: function (data) {

            if (data.ok === true) {
                
                alert('success');

            } else {

                alert('Upload failed');

            }

        },
        error: function (e) {
            console.log(e);
            alert('Upload failed');

        }
    });

});

In my case, there's no "submit" button or something - the file will upload immediately when you select it in your file selection dialog.

Now let's deal with the backend. In my views.py file of the app, I add the following script:


from django.core.files.storage import FileSystemStorage
from django.conf import settings
import uuid

@login_required
@csrf_exempt
def ProfilePicUpload(request):
    details = get_object_or_404(UserData, user=request.user)
    try:

        if request.method == 'POST':
        
            #This is where it grabs your file from the Ajax POST.
            myfile = request.FILES['file']
      
      
            
            #Lets generate a UUID string to make sure all files remain unique named.
            id_1 = str(uuid.uuid1())
            
            
            # This is where we will save it.
            fs = FileSystemStorage(location='/media')

            # In order to rename, lets get the file extension.
            file_extension = os.path.splitext(myfile.name)[1]
            
            
            newfilename = id_1 + file_extension
            # Cool, file renamed, now lets rename
            filename = fs.save(newfilename, myfile)
            
            file_url = fs.url(filename)
            #This gets the directory / url of the saved file.
            print(file_extension)

            print(file_url)

            #This saves the file directory in the database.

            details.profilepic = file_url
            details.save(update_fields=["profilepic"])

            return JsonResponse({"ok": True, "url": file_url, "filename": myfile.name})

    except Exception as e:
        print(e)
        return JsonResponse({'ok': False})


Oh, the model field for this is a filefield and looks like this.

profilepic = models.FileField(null=True, blank=True)

Lastly, we need to update the URLS to the function. So in urls.py:

   path('profilepicupload', views.ProfilePicUpload, name="upload"),

Also, this is assuming you're using Django 2.0 and above.

At last it works and I can upload stuff!

← Back to article list