Django JQuery File Upload

I'm sure there must be a really nice packaging of this, but so far all I've found is example projects that don't quite do what I'd want... so here's the basic setup; you have a Django model with File fields that you want to convert to using JQuery File Upload.  For now, we'll assume you want to do this with your own UI, so here's what it looks like:

{% extends 'master.html' %}
{% block content %}
<h1>File Upload Test</h1>

<form class='upload-form movie' method="POST" action="{% url upload_movie %}" enctype="multipart/form-data">
<fieldset><legend>Movie Upload</legend>
<div class="progress"><span class="complete"></span></div>
{% comment %} Equivalent to: {{ movie_form }} {% endcomment %}
{% with movie_form as form %}
{% include "formfields.html" %}
{% endwith %}
</fieldset>
</form>
{% endblock %}

{% block page_templates %}
<div class="uploading-file">
<div class="status">Uploading</div>
</div>
{% endblock %}

{% block page_scripts %}
{% comment %}JQuery UI Version of File Upload Plugin

https://github.com/blueimp/jQuery-File-Upload/tree/master/js
{% endcomment %}
<script src="{{STATIC_URL}}js/vendor/jquery.ui.widget.js"></script>
<script src="{{STATIC_URL}}js/vendor/jquery.iframe-transport.js"></script>
<script src="{{STATIC_URL}}js/vendor/jquery.fileupload.js"></script>

<script type="text/javascript">
$(document).ready( function() {
$('.upload-form').each( function() {
var form = $(this);
/* assumes you decorate each field with a class of field-fieldname */
var file_field = form.find( '.field-file input' );
var progress = form.find( '.progress' );
var find_progress = function( fileInput ) {
/* find the project info related to this fileInput instance */
var result = null;
progress.find( '.uploading-file' ).each( function() {
if ($(this).data( 'fileInput' ) === fileInput){
result = $(this);
}
});
return result;
};
file_field.fileupload({
dataType: 'json',
url: form.attr( 'action' ),
form: form,
dropZone: form,
add: function( e, data ) {
var this_status = $('.templates .uploading-file').clone();
progress.append( this_status );
this_status.data( 'fileInput', data.fileInput );
data.submit();
},
done: function (e, data) {
var status = find_progress( data.fileInput );
if (status) {
$.each(data.result, function (index, file) {
if (file.error) {
status.html( 'Upload Failed' );
if (file.error[field]) {
/* show your errors to your users */
status.addClass( 'error' );
})
} else {
$('<p/>').text(file.name).appendTo(status);
status.addClass( 'success' );
}
});
status.fadeOut( 5000, function() {
status.remove();
});
/* You'll likely need to refresh your local data now */
} else {
console.log( "Got a done for a file that didn't upload" );
}
},
fail: function( e, data ) {
var status = find_progress( data.fileInput );
if (status) {
status.html( 'Upload failed '+data.textStatus );
}
}
});
});
});
</script>
{% endblock %}

Which takes your rendered ModelForm and renders it into the HTML, allowing you to submit whatever other fields you need (including hidden and csrf-protection fields) for your file upload.  Note that there's no submit/button here.  The file-upload plugin considers "select a file" to be a submit event.  You could defer the event by storing the "data" parameter in the "add" callback and then calling data.submit() in your button/submit handler for your form.  In any event, the js should likely *hide* the submit when activated so that regular click-to-submit uploading would still work.

The back-end is pretty similar to what you're familiar with if you do file model-form handling;

@render_to_json
def upload_movie( request, movie=None ):
if request.method == 'POST':
form = forms.MovieForm( request.POST, files=request.FILES,instance=movie )
if form.is_valid():
movie = form.save()
if request.is_ajax():
return [{
"name": os.path.basename(movie.file.name),
"size": form.cleaned_data['file'].size,
"type": movie.content_type,
}]
else:
messages.success( request, _("Movie Uploaded"))
else:
if request.is_ajax():
return [{
'error': dict([
(k,unicode(v))
for (k,v) in form.errors.items()

]),
}]
else:
messages.error( request, _("There was an error uploading your movie: %(error)s")%{
'error':unicode(form.errors.items())
})
return HttpResponseRedirect( reverse('content'))

Anyway, hope that's useful for someone some day.  This was a rabbit-hole I ran down too far in trying to achieve a very minor effect.

Comments

Comments are closed.

Pingbacks

Pingbacks are closed.