How to use flask framework to render the html, send JSON data and have ajax update table

How to use flask framework to render the html, send JSON data and have ajax update table

gammaguygammaguy Posts: 2Questions: 1Answers: 0

I was trying the Ajax data source (objects) example -- https://datatables.net/examples/ajax/objects.html. It worked as expected including running it via flask.

Rather than filling the table from a JSON file I would like to use flask to render the html and provide the JSON data to ajax to fill the table.

Flask starts to render the page except where the data is supposed to be it says loading. I also get a popup that says 127.0.0.1:5000 says DataTables warning: table id=example - Invalid JSON response. For more information about this error, please see http://datatables.net/tn/1 I tried the link but it didn't have any additional information. I tried various things but no change. Any ideas?

# flaskText.py
from flask import Flask, render_template, jsonify
import json
# Assume data comes from somewhere else
data = {
  "data": [
    {
      "id": "1",
      "name": "Tiger Nixon",
      "position": "System Architect",
      "salary": "$320,800",
      "start_date": "2011/04/25",
      "office": "Edinburgh",
      "extn": "5421"
    },
    {
      "id": "2",
      "name": "Garrett Winters",
      "position": "Accountant",
      "salary": "$170,750",
      "start_date": "2011/07/25",
      "office": "Tokyo",
      "extn": "8422"
    }]
}

app = Flask(__name__)

@app.route('/index')
@app.route('/')
def index():
  # return render_template('index.html',data=data)
  return render_template('index.html',data=json.dumps(data))

if __name__ == '__main__':
  app.run(debug=True)
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
  <title>Datatables Example</title>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.18/css/jquery.dataTables.min.css"/>
  <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
  <script src="https://cdn.datatables.net/1.10.18/js/jquery.dataTables.min.js"></script>
</head>
<body>
<h1>My First Heading</h1>
<table id="example" class="display" style="width:100%">
        <thead>
            <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Extn.</th>
                <th>Start date</th>
                <th>Salary</th>
            </tr>
        </thead>
        <tfoot>
            <tr>
                <th>Name</th>
                <th>Position</th>
                <th>Office</th>
                <th>Extn.</th>
                <th>Start date</th>
                <th>Salary</th>
            </tr>
        </tfoot>
    </table>
<script>
$(document).ready(function() {
    $('#example').DataTable( {
        "ajax": {
            // "url": "static/objects2.txt", // This works for the static file
            "url": "/index", // This doesn't work
            "dataType": "json",
            // "dataSrc": "data",
            // "contentType":"application/json"
        },
        "columns": [
            { "data": "name" },
            { "data": "position" },
            { "data": "office" },
            { "data": "extn" },
            { "data": "start_date" },
            { "data": "salary" }
        ]
    } );
} );
</script>
</body>
</html>

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975
    Answer ✓

    The problem is you can't return the web page and data at the same time for Datatables. You will need to return the web page, it will load and Datatables will initialize then send the ajax request. This will need to be a different URL that will simply return the JSON data. You will have two Python functions; index and dt_index (or whatever you want to call the URL for the ajax request).

    Kevin

  • gammaguygammaguy Posts: 2Questions: 1Answers: 0

    Thank you so very much @kthorngren . I have literally been trying to get this to work for 40+ hours.

    I have included the full solution below:

    flaskTest.py

    from flask import Flask, render_template, jsonify
    
    app = Flask(__name__)
    
    @app.route('/index')
    @app.route('/')
    def index():
      return render_template('index.html')
    
    @app.route('/index_get_data')
    def stuff():
      # Assume data comes from somewhere else
      data = {
        "data": [
          {
            "id": "1",
            "name": "John Q Public",
            "position": "System Architect",
            "salary": "$320,800",
            "start_date": "2011/04/25",
            "office": "Edinburgh",
            "extn": "5421"
          },
          {
            "id": "2",
            "name": "Larry Bird",
            "position": "Accountant",
            "salary": "$170,750",
            "start_date": "2011/07/25",
            "office": "Tokyo",
            "extn": "8422"
          }]
      }
      return jsonify(data)
    
    
    if __name__ == '__main__':
      app.run()
    

    /templates/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <title>Datatables Example</title>
      <meta charset="utf-8">
      <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.18/css/jquery.dataTables.min.css"/>
      <script src="https://code.jquery.com/jquery-3.3.1.js"></script>
      <script src="https://cdn.datatables.net/1.10.18/js/jquery.dataTables.min.js"></script>
    </head>
    <body>
    <h1>My Heading</h1>
    <table id="example" class="display" style="width:100%">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Position</th>
                    <th>Office</th>
                    <th>Extn.</th>
                    <th>Start date</th>
                    <th>Salary</th>
                </tr>
            </thead>
            <tfoot>
                <tr>
                    <th>Name</th>
                    <th>Position</th>
                    <th>Office</th>
                    <th>Extn.</th>
                    <th>Start date</th>
                    <th>Salary</th>
                </tr>
            </tfoot>
        </table>
    <script>
    function setupData() {
        $(document).ready(function () {
            $('#example').DataTable({
                "ajax": {
                    // "url": "static/objects2.txt", // This works for the static file
                    "url": "/index_get_data", // This now works too thanks to @kthorngren 
                    "dataType": "json",
                    "dataSrc": "data",
                    "contentType":"application/json"
                },
                "columns": [
                    {"data": "name"},
                    {"data": "position"},
                    {"data": "office"},
                    {"data": "extn"},
                    {"data": "start_date"},
                    {"data": "salary"}
                ]
            });
        });
    }
    $( window ).on( "load", setupData );
    </script>
    </body>
    </html>
    
  • etilleyetilley Posts: 31Questions: 4Answers: 0

    This works to paint the table, but this approach does not permit the code needed to click on rows and add a reaction to those clicked table lines.

    Ordinarily that code is installed after the $().DataTable({ bracket in the $(document).ready function ... but it doesn't work because this code is only used for loading.

    How do we work with the lines once loaded?

  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    You could add that code inside initComplete, that would ensure the table is loaded and ready.

    Colin

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    Thanks @colin ; I added the initComplete but was unclear how to place it and I can't get it to alert or drive work based on single row clicks. Here's what I have so far ...

        function setupData() {
          $(document).ready(function () {
              $('#example').DataTable({
                  "ajax": {
                      // "url": "static/objects2.txt", // This works for the static file
                      "url": "/indlist", // This now works too thanks to @kthorngren
                      "dataType": "json",
                      "dataSrc": "",
                      "contentType":"application/json"
                  },
                  "columns": [
                      {"data": "id"},
                      {"data": "code"},
                      {"data": "label"},
                      {"data": "desc"},
                      {"data": "score"},
                      {"data": "sheetscore"}
                  ],
                  "select": {
                    style: 'single'
                  },
                  "initComplete": function( settings, json ) {
                    // This approach suggested by https://datatables.net/reference/option/initComplete
                    $('#example tbody').on( 'click', 'tr', function () {
                      // this came from https://datatables.net/examples/advanced_init/events_live.html
                      var data = table.row( this ).data();
                        alert( 'You clicked on '+data[0] );
    
                      if ( $(this).hasClass('selected') ) {
                        $(this).removeClass('selected');
                      }
                      else {
                          table.$('tr.selected').removeClass('selected');
                          $(this).addClass('selected');
                      }
    
                      //document.getElementById('randomizeData').addEventListener('click', function() {
                      lineChartData.datasets.forEach(function(dataset) {
                        dataset.data = dataset.data.map(function() {
                          return randomScalingFactor();
                        });
                      });
    
                      window.myLine.update();
    
                    } );
                  }
              });
    
          });
        }
        $( window ).on( "load", setupData );
    
    
    
                // $('#example').DataTable();
                // taken from https://datatables.net/examples/api/select_single_row.html
                // Select a single row to drive reports
        // var table = $('#example').DataTable( { 
        //   responsive: true, 
        // });
    
        $('#button').click( function () {
            table.row('.selected').remove().draw( false );
        } );
    
  • etilleyetilley Posts: 31Questions: 4Answers: 0

    Sorry to take so long to respond to your note as well. I just saw it this evening. Cheers

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    I suspect if you look at the browser's console you will see an error complaining about something being undefined. You have this:

                $('#example tbody').on( 'click', 'tr', function () {
                  // this came from https://datatables.net/examples/advanced_init/events_live.html
                  var data = table.row( this ).data();
                    alert( 'You clicked on '+data[0] );
    

    The variable table has not been defined. You can get an instance of the API in the click event like this:

                $('#example tbody').on( 'click', 'tr', function () {
                  // this came from https://datatables.net/examples/advanced_init/events_live.html
                  var table = $('#example').DataTable();
                  var data = table.row( this ).data();
                    alert( 'You clicked on '+data[0] );
    

    You are using data[0] but you have defined your data using objects so you will need data.id instead.

    Kevin

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    How to work in the setupData call with this approach though?

    function setupData() {
    ?
    }

    $( window ).on( "load", setupData );

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    This doesn't load the table...

    function setupData() {
          $('#example tbody').on( 'click', 'tr', function () {
            // this came from https://datatables.net/examples/advanced_init/events_live.html
    
            $var table = $('#example').DataTable({
                "ajax": {
                    // "url": "static/objects2.txt", // This works for the static file
                    "url": "/indlist", // This now works too thanks to @kthorngren
                    "dataType": "json",
                    "dataSrc": "",
                    "contentType":"application/json"
                },
                "columns": [
                    {"data": "id"},
                    {"data": "code"},
                    {"data": "label"},
                    {"data": "desc"},
                    {"data": "score"},
                    {"data": "sheetscore"}
                ],
                "select": {
                  style: 'single'
                }
                // },
                // "initComplete": function( settings, json ) {
                //   // This approach suggested by https://datatables.net/reference/option/initComplete
    
                // }
            });  
    
            var data = table.row( this ).data();
              // alert( 'You clicked on '+data[0] );
              alert( 'You clicked on '+ data.id );
    
            if ( $(this).hasClass('selected') ) {
              $(this).removeClass('selected');
            }
            else {
                table.$('tr.selected').removeClass('selected');
                $(this).addClass('selected');
            }
    
    // other reactive to be called per each selected row
            window.myLine.update();
    
            } );  
           };
    
          $( window ).on( "load", setupData );
    
    
        $('#button').click( function () {
            table.row('.selected').remove().draw( false );
        } );
    
  • colincolin Posts: 15,240Questions: 1Answers: 2,599

    We're happy to take a look, but as per the forum rules, please link to a test case - a test case that replicates the issue will ensure you'll get a quick and accurate response. Information on how to create a test case (if you aren't able to link to the page you are working on) is available here.

    Cheers,

    Colin

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975
    edited April 2020

    function setupData() {
    $('#example tbody').on( 'click', 'tr', function () {

    I didn't intend for you to move the this line. With that you did you are creating a click event with setupData(). In order to load the table you will need to click on the table. I expected you to add the line var table = $('#example').DataTable(); to your click event to get an instance of the Datatables API as described here. Like this:

    function setupData() {
     
            var table = $('#example').DataTable({
                "ajax": {
                    // "url": "static/objects2.txt", // This works for the static file
                    "url": "/indlist", // This now works too thanks to @kthorngren
                    "dataType": "json",
                    "dataSrc": "",
                    "contentType":"application/json"
                },
                "columns": [
                    {"data": "id"},
                    {"data": "code"},
                    {"data": "label"},
                    {"data": "desc"},
                    {"data": "score"},
                    {"data": "sheetscore"}
                ],
                "select": {
                  style: 'single'
                }
                // },
                // "initComplete": function( settings, json ) {
                //   // This approach suggested by https://datatables.net/reference/option/initComplete
     
                // }
            }); 
     
          $('#example tbody').on( 'click', 'tr', function () {
            // this came from https://datatables.net/examples/advanced_init/events_live.html
            var table = $('#example').DataTable();
            var data = table.row( this ).data();
              // alert( 'You clicked on '+data[0] );
              alert( 'You clicked on '+ data.id );
     
            if ( $(this).hasClass('selected') ) {
              $(this).removeClass('selected');
            }
            else {
                table.$('tr.selected').removeClass('selected');
                $(this).addClass('selected');
            }
     
    // other reactive to be called per each selected row
            window.myLine.update();
     
            } ); 
           };
     
          $( window ).on( "load", setupData );
     
     
        $('#button').click( function () {
            table.row('.selected').remove().draw( false );
        } );
    

    Also you update this line:
    $var table = $('#example').DataTable({

    The leading $ will cause a syntax error. Make sure to remove it as shown in my updated code.

    Kevin

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    @kthorngren worked like a charm - thanks Kevin.

    I think this will be a frequently referenced post because I searched for a solution for three weeks and there wasn't a single reference to database-driven selects.

    This has to be a common requirement of every reactive webpage...

  • etilleyetilley Posts: 31Questions: 4Answers: 0
    edited April 2020

    @kthorngren - A side note, is that the test case tools are great but I don't have a production url for the data ("url": "/indlist"). Is there a trick that emulates the json data returned from the ajax database call?

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    I think there are some Javascript ajax emulators but for most "test cases" I don't think that is needed. Unless the problem being worked is ajax related most of the time Javascript data is good enough. Here is en example. Many times you can just get a sample of the JSON response and apply it to Datatables using data.

    You can also use ajax as a functon. There is a simple example in the docs. This Scroller example shows how you can simulate using ajax. But there is no ajax request sent.

    HTH,
    Kevin

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    Great examples - much obliged. I'm new to javascript so even though this might seem obvious to developers, it's a big help for noobs (like me :) ...

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    We all started at the noob level :smile: Glad you got it working.

    Kevin

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    @kthorngen This table syntax page is generating the error:
    [Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952

    Do you have any ideas on what could be causing this ?

  • etilleyetilley Posts: 31Questions: 4Answers: 0

    https://stackoverflow.com/questions/37721782/what-are-passive-event-listeners discusses the issue with a solution that crashes Mozilla browsers ...

  • kthorngrenkthorngren Posts: 21,449Questions: 26Answers: 4,975

    This table syntax page is generating the error:

    What syntax are you referring to? Is there an example we can look at?

    Kevin

This discussion has been closed.