Zain Khan
Zain Khan

Reputation: 3793

Django: How to upload a file using ajax

I am using django 1.5, python 2.7 and jquery 1.9. I have a form which has precisely 2 fields i.e. title and document. When I press submit I want the users chosen document to be present in the request.FILES as shown in the view.

When I submit the regular form (without ajax), this works fine, but with ajax I do not get the file field in my request. Any suggestions on how to upload a file using ajax.

HTML:

<form enctype="multipart/form-data" action="{% url 'upload_document' %}" method="post" id="uploadForm">
    {% csrf_token %}
        <ul>
          <li>
            <div>Title</div>
            <input id="title" type="text" maxlength="200"/>
            <div class="error"></div>          
          </li>

          <li>
            <div>Upload File</div>
            <input id="document" type="file" size="15" />
            <div class="error"></div>
          </li>
        </ul>

      <input type="submit" value="submit"/></p>
</form>   

FORMS.PY:

class UploadForm( forms.Form ):
    document = forms.FileField()
    title = forms.CharField(max_length = 200)

    def clean(self):
        cleaned_data = super(UploadForm, self).clean()
        return cleaned_data

    def save(self, *args, **kwargs):
        title = self.cleaned_data['title']
        doc = self.cleaned_data['document']

        document = Document(title = title, document = doc)
        document.save()
        return document

SCRIPT:

<script type="text/javascript">
    $("#uploadForm").submit(function(event){
      event.preventDefault();
      $.ajax({
        url : "{% url 'upload_document' %}",
        type: "POST",
        data : {csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
                title: document.getElementById('title').value,
                //document: document: document.getElementById('document'),
        },
        dataType : "json",
        success: function( response ){
           if(response == "True"){
            // success
           }
           else {
            //append errors
           }
        }
      });
  }); 
</script>

VIEWs.PY

def upload_document(request): 
    print request.POST
    print request.FILES
    if request.is_ajax():
        if request.method == 'POST':
            form = UploadForm(request.POST, request.FILES, user = request.user)

            if form.is_valid():
                form.save()
                return HttpResponse(simplejson.dumps('True'), mimetype = 'application/json' )
            else:
                errors = form.errors
                return HttpResponse(simplejson.dumps(errors), mimetype = 'application/json' )

Upvotes: 4

Views: 9563

Answers (2)

sarathkumar P M
sarathkumar P M

Reputation: 131

I think the issue is in the submit button, change it into normal button ie, <button type='button' id='submit'>submit</button>(by default all buttons in form are submit) and the ajax as

$('#submit').on('click',function(){
    frm = $(this).parents('form')
    $.ajax({
          type: frm.attr('method'),
          dataType:'json',
          url: frm.attr('action'),
          data: frm.serialize(),
          async: false,
          success: function (data) {
              console.log('success')
          },
          error: function(data) {
              console.log("Something went wrong!");
          }
})

All others will be same Just try it will work

Upvotes: 0

freakish
freakish

Reputation: 56587

The answer to that question is not that simple. First of all if you intend to support old browsers then indeed it gets nasty. You have to deal with hidden iframes and some JavaScript tricks. I do advice using some well-known scripts for that like jQuery-File-Upload.

But the world is evolving and new technologies arise including HTML5. There's a new File API which is available in most modern browsers ( IE10+, FireFox3.6+, Chrome13+, see: http://caniuse.com/fileapi ) which can be used for that. First you need some HTML:

<input type="file" id="file-select" />

Then you can bind to (for example) change event:

$('#file-select').change( handleFileSelect );

and finally the handler itself:

var data = {};

function createReaderHandler(name) {
  return function(ev) {
    data[name] = ev.target.result;
  };
}

function handleFileSelect(ev) {
  var files = ev.target.files; // FileList object

  // Loop through the FileList
  for (var i = 0; i < files.length; i++) {
    var file = files[i],
        name = file.name || file.fileName,
        reader = new FileReader();

    reader.onload = createReaderHandler(name);
    reader.readAsText(file);
  }
}

Once the data is loaded into JavaScript memory (note that the operation is asynchronous) you can send it via AJAX like any other data. There are more options: depending on your file you can read it as a binary data using .readAsBinaryString and so on. Google is your friend. :)

Also I think there already are good scripts for uploading files with a fallback to old methods. This one can be interesting (haven't tried it):

http://www.plupload.com/

Upvotes: 3

Related Questions