Search code examples
javascriptflaskhandsontable

Saving HandsOnTable data with Flask


I'm trying to load and save data from a HandsOnTable using Flask. I'm following these instructions for loading and saving (retrieving and sending) data using ajax. I have managed to get data to load into the table from a URL that returns a JSON dictionary, but I haven't figured out how to send data do store in my database.

Here is what the relevant part of the javascript looks like:

Handsontable.Dom.addEvent(save, 'click', function() {
    // save all cell's data
    ajax('/json/save.json', 'GET', JSON.stringify({data: hot.getData()}), function (res) {
      var response = JSON.parse(res.response);

      if (response.result === 'ok') {
        exampleConsole.innerText = 'Data saved';
      }
      else {
        exampleConsole.innerText = 'Save error';
      }
    });
  });

So hopefully that is taking the data from the HandsOnTable, turning it into a big JSON table of this format:

{'data' : [[row 1], [row 2],...]}

And here is what the relevant Flask view function looks like:

@app.route('/json/save.json', methods = ['GET', 'POST'])
@login_required
def jsonSave():
    data = request.form['data']
    #Do stuff to load data into database
    return 'ok'

With irrelevant parts removed. Basically my question is how do I make the data = request.form['data'] part of the save function work, and turn it into a simple list of rows?

Incidentally, part of why this is difficult is that I can't see what exactly is being sent to the view function with the ajax call. Is there a way I can see that so I can more easily debug issues like this? Print statements don't seem to work in view functions (I can't see them in the console).

Thanks a lot, Alex

Update Changed (again) as per ZekeDroid's instructions to:

Handsontable.Dom.addEvent(save, 'click', function() {
// save all cell's data
console.log(JSON.stringify({data: hot.getData()}));
ajax('/json/save/{{well['id']}}', 'POST', JSON.stringify({data: hot.getData()}), function (res) {
  var response = JSON.parse(res.response);

  if (response.result === 'ok') {
    exampleConsole.innerText = 'Data saved';
  }
  else {
    exampleConsole.innerText = 'Save error';
  }
});

});

and

@app.route('/json/save/<int:well_id>', methods = ['GET', 'POST'])
@login_required
def jsonSave(well_id):
    jsonData = request.get_json()
    print 'jsonData:', jsonData
    data = jsonData['data']
    print 'data:', data
    #Insert table into database
    print 'saving', well_id
    return json.dumps(True)

Debug output: Basically, it looks like Flask is not loading a json object when it calls jsonData = request.get_json(). The console.log(JSON.stringify({data: hot.getData()})); looks ok, however.

Here is the output from the browser and Flask consoles:

Browser:

{"data":[["01/01/15",100,300,200,96],["01/02/15",200,500,300,50],["01/03/15",300,600,400,80],["01/01/15",100,300,200,96],["01/02/15",200,500,300,50],["01/03/15",300,600,400,80],["01/01/15",100,300,200,96],["01/02/15",200,500,300,50],["01/03/15",300,600,400,80],[null,null,null,null,null]]}
samples.js:94 POST http://127.0.0.1:5000/json/save/1 500 (INTERNAL SERVER ERROR)

Flask:

jsonData: None
127.0.0.1 - - [13/May/2015 11:41:31] "POST /json/save/1 HTTP/1.1" 500 -
Traceback (most recent call last):
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1817, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask\app.py", line
1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\aschmitt\Envs\PetroTools\lib\site-packages\flask_login.py", lin
e 758, in decorated_view
    return func(*args, **kwargs)
  File "C:\Users\aschmitt\Dropbox\Python\PetroTools\app\views.py", line 236, in
jsonSave
    data = jsonData['data']
TypeError: 'NoneType' object has no attribute '__getitem__'

Solution

  • To solve this problem, I started over. I included these javascript files:

    <script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
    <link rel="stylesheet" media="screen" href="http://handsontable.com/dist/handsontable.full.css">
    <script src="http://handsontable.com/dist/handsontable.full.js"></script>
    

    I removed the samples.js file that I got from the old jsfiddle. I think it was messing things up.

    As per ZekeDroid's suggestion, I changed the flask save function to this:

    @app.route('/json/save/<int:well_id>', methods = ['GET', 'POST'])
    @login_required
    def jsonSave(well_id):
        w = Well.query.get(well_id)
        jsonData = request.get_json()
        print 'jsonData:', jsonData
        data = jsonData['data']
        print 'data:', data
    
        #Load Data dictionary into the database.
    
        return json.dumps(True)
    

    I replaced the jquery save function with this:

    Handsontable.Dom.addEvent(save, 'click', function() {
        // save all cell's data
        var arr = { data: hot.getData()};
        $.ajax({
        url: '/json/save/{{well['id']}}',
        type: 'POST',
        data: JSON.stringify(arr),
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        async: true,
        success: function(msg) {
            console.log(msg);
          }
          });
    });
    

    I couldn't get the save function from the handsontable tutorial (here) to work, so I used the accepted answer here instead. I used POST because if I understand correctly, it doesn't have a size limit like GET does, and I'm going to have some pretty big arrays being sent.

    Removing the samples.js file broke the load function as well, so I replaced that with this:

    Handsontable.Dom.addEvent(load, 'click', function() {
        var arr = { data: []};
        $.ajax({
        url: '/json/load/{{well['id']}}',
        type: 'GET',
        contentType: 'application/json; charset=utf-8',
        data: JSON.stringify(arr),    
        dataType: 'json',
        async: true,
        success: function(msg) {
            hot.loadData(msg.data);
            console.log('Loaded:');
            console.log(msg.data);
        }
        });
    });
    

    Which is obviously very similar to the save function. Thanks for all the help ZekeDroid.

    Alex