diy-django ajax file upload progress widget

The Field, Widget, and Javascript

Create an UploadField and UploadWidget like this:

from django.forms import fields, widgets
from django.utils.safestring import mark_safe


class UploadWidget(widgets.TextInput):

    def render(self, name, value, attrs=None):
        attrs = attrs or {}
        attrs['class'] = 'vTextField'
        input = super(UploadWidget, self).render(name, value, attrs=attrs)
        value = ('<a href="%s" target="_blank">view</a>' % value.url) if value and getattr(value, 'url') else value
        onchange = r'''
f = this.files[0]
this.value = ''
var xhr = new XMLHttpRequest()
xhr.open('POST', '/simple_upload/' + f.name, true)
xhr.setRequestHeader('X-CSRFToken', this.form.csrfmiddlewaretoken.value)
xhr.upload.status = this.parentNode.firstChild.nextElementSibling // <span>
xhr.upload.status.innerHTML = 'pending...'
xhr.upload.onprogress = function(e){ this.status.innerHTML = Math.round(100 * e.loaded / e.total) + '% uploaded' }
xhr.send(f)
xhr.onload = function(){
  this.upload.status.innerHTML = '<a href=\'/media/' + encodeURI(this.responseText) + '\' target=_blank>view</a>'
  this.upload.status.previousElementSibling.value = this.responseText // <input type=hidden>
}'''
        assert '"' not in onchange
        return mark_safe('<p>%s<span>%s</span><br><input type="file" onchange="%s" value="upload"></p>' % (input, value, onchange))


class UploadField(fields.CharField):
    widget = UploadWidget

The backend view to receive the file upload

In views.py:

from django.contrib.admin.views.decorators import staff_member_required
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.http import HttpResponse


@staff_member_required
def simple_upload(request, name):
    return HttpResponse(default_storage.save(name, ContentFile(request.body)), 'text/plain')

In urls.py:

from . import views
urlpatterns = [
    url(r'^simple_upload/(.+)$', simple_upload),
]

Using the FileUpload field in the admin

In admin.py:

class MyAdmin(admin.ModelAdmin):
    formfield_overrides = {models.FileField: {'form_class': UploadField}}