1

Trying to understand what I'm doing wrong here when using JQuery AJAX POST request to send both form data and an attached file to a Flask endpoint.

The basic Flask view that I'm still building looks like this:

@main.route('/videoupload', methods=['GET','POST'])
def videoupload():
    if request.method == 'POST':
        ajaxpost = request.form['m_data']
        print(ajaxpost)
    return render_template('videoupload.html')

The JQuery for the form and attached file looks like this:

$("#submit_btn").click(function() {  
        var proceed = true;

        if(proceed) //everything looks good! proceed...
        {
            //data to be sent to server
            var m_data = new FormData();    
            m_data.append( 'video_title', $('input[name=videoTitle]').val());
            m_data.append( 'video_description', $('input[name=videoDescription]').val());
            m_data.append( 'video_tags', $('input[name=videoTags]').val());
            m_data.append( 'file_attach', $('input[name=file_attach]')[0].files[0]);
            //instead of $.post() we are using $.ajax()
            //that's because $.ajax() has more options and flexibly.
            $.ajax({
              url: '/videoupload',
              data: m_data,
              processData: false,
              contentType: false,
              type: 'POST',
                  //dataType:'json',
              datatype:'json',
              success: function(response){
                 //load json data from server and output message     
                if(response.type == 'error'){ //load json data from server and output message     
                    output = '<div class="error">'+response.text+'</div>';
                }else{
                    output = '<div class="success">'+response.text+'</div>';
                }
                $("#videoform #form_results").hide().html(output).slideDown();
              }
            });        
        }
    });

Using Firebug and the Net window, I can confirm that data entered in the fields of the form and the attached file are being appended to the FormData() object.

When the user clicks Submit button, I get the following error in the Console:

> POST http://127.0.0.1:8000/videoupload 400 (BAD REQUEST)
send @ jquery.js:9664
m.extend.ajax @ jquery.js:9215
(anonymous function) @ videoupload:137
n.event.dispatch @ jquery.min.js:3
r.handle @ jquery.min.js:3

Navigated to http://127.0.0.1:8000/videoupload?videoTitle=asdf&videoDescription=asdfasdfasdfasdfasdf&videoTags=ZcZXcZXcZXcZXC&file_attach=ScreenCaptureProject1.mp4

In the Terminal window running Flask app (using Gunicorn) running in debug mode, no errors appear:

[2016-05-20 00:18:21 -0400] [27033] [DEBUG] POST /videoupload
POST CALLED
[2016-05-20 00:18:24 -0400] [27033] [DEBUG] GET /videoupload

It seems as though the AJAX is pinging the Flask view with a POST request. Am I handling the form incorrectly in the Flask view? Is there something incorrect with the JQuery AJAX POST request that Flask doesn't like?

AdjunctProfessorFalcon
  • 1,790
  • 6
  • 26
  • 62

1 Answers1

1

request.form is a MultiDict with the key-value pairs of the received form data.

You are attempting to index this dictionary using the string 'm_data' as a key, but it looks like 'm_data' is just the name of your JavaScript variable and not actually a key of the form data. If 'm_data' is not a valid key this would raise an exception.

To quote the Werkzeug documentation on MultiDicts:

"From Werkzeug 0.3 onwards, the KeyError raised by this class is also a subclass of the BadRequest HTTP exception and will render a page for a 400 BAD REQUEST if caught in a catch-all for HTTP exceptions."

To help debug this route, you can wrap your python code in a try-except block:

@main.route('/videoupload', methods=['GET','POST'])
def videoupload():
    if request.method == 'POST':
        try:
            [your code]
        except Exception, e:
            print e
    return render_template('videoupload.html')

Then you can check whether a KeyError appears in the error logs (or console if you're running the Flask test server locally).

A. Vidor
  • 2,502
  • 1
  • 16
  • 24
  • Thanks for the background info and doc links. Unfortunately the error try/except catches isn't that informative, just getting 400: Bad Request. However, when I try `ajaxpost = request.form['video_title']` I get `undefined` in the console. So maybe I just need to keep digging into how to properly access the key/values in `m_data` object – AdjunctProfessorFalcon May 20 '16 at 17:34
  • 1
    You can `print` the dictionary `request.form` to see what Flask has stored there. – A. Vidor May 20 '16 at 22:06
  • Yeah, tried that earlier today and it definitely helped. It's empty: `ImmutableMultiDict([('videoTitle', u'undefined'), ('videoTags', u'undefined'), ('videoDescription', u'undefined')])` Interestingly, all the values are winding up on the url of the same page as a query string, like the AJAX is adding all the values that should be in the immutablemultidict to the URL – AdjunctProfessorFalcon May 20 '16 at 22:23
  • Take a look at [this thread](http://stackoverflow.com/questions/6974684/how-to-send-formdata-objects-with-ajax-requests-in-jquery). It looks like you may have to explicitly set the `Content-Type` header to `'multipart/form-data'` when configuring the AJAX request. If you don't mind jQuery serializing the data and passing it as a query string, you should examine `request.values` [(see docs)](http://flask.pocoo.org/docs/0.10/api/#flask.Request.values) rather than `request.form`. – A. Vidor May 21 '16 at 22:38