2

Let me explain my end goal and then maybe that will paint a picture of what I'm trying to do. Here is what my application needs to do:

  1. End user goes to http://www.example.com/ms_bulletins and they see a drop down menu with selectable dates.
  2. Once they select a date, the date is sent to my flask backend and the date is used to query the database for bulletins from that date.
  3. The bulletins should then return to http://www.example.com/ms_bulletins and the table should populate.

I actually have this working almost completely, except for the page does not load to show me the data. Here is some snippets of code:

JavaScript

function dateClick(date){
  var dropdown = document.getElementById("date");
  var date = dropdown.options[dropdown.selectedIndex].value;
  console.log( "Date: " + date );
  $.getJSON($SCRIPT_ROOT + '/ms_bulletins', {
            date: date,
  });
}

Flask

@application.route("/ms_bulletins")
def ms_bulletins():
    date = request.args.get('date', None, type=str)
    session = Session()
    data = session.query(MsBulletins).filter(MsBulletins.date==date)
    bulletins = data.all()
    return render_template("ms_bulletins.html", bulletins=bulletins)

HTML

<script>
$(document).ready( function () {
   $('#bulletin_table').DataTable();
} );
</script>

<div id="select_bulletin_date" onchange="dateClick(this)">
<form>
  <fieldset>
  <legend class="header">MS Bulletin Summary:</legend>
  <select id="date">
    <option name="date" value="11/8/2016">November 2016</option>
    <option name="date" value="10/11/2016">October 2016</option>
  </select>
  </fieldset>
</form>
</div>
<hr>
<div id="ms_bulletins">
<table id="bulletin_table" class="display">
<thead>
    <tr>
        <th>Bulletin ID</th>
        <th>Bulletin KB</th>
        <th>Bulletin Name</th>
        <th>Bulletin Date</th>
    </tr>
</thead>
<tbody>
  {% for bulletin in bulletins %}
    <tr>
        <td><a href="https://technet.microsoft.com/en-us/library/security/{{ bulletin.bulletin_id }}" target="_blank">{{ bulletin.bulletin_id }}</a></td>
        <td><a href="https://support.microsoft.com/en-us/kb/{{ bulletin.kb }}" target="_blank">{{ bulletin.kb }}</a></td>
        <td>{{ bulletin.title }}</td>
        <td>{{ bulletin.date }}</td>
    </tr>
  {% endfor %}
</tbody>
</table>
</div>

Currently when the user goes to http://www.example.com/ms_bulletins, the table is blank, then the user selects a date, I can see the date being passed in the console, and then nothing happens.

If I view the source after the "render template" should have taken place, I can see in the source code that it actually worked:

Source Code

<table id="bulletin_table" class="display">
  <thead>
    <tr>
      <th>Bulletin ID</th>
      <th>Bulletin KB</th>
      <th>Bulletin Name</th>
      <th>Bulletin Date</th>
   </tr>
  </thead>
<tbody>
   <tr>
    <td><a href="https://technet.microsoft.com/en-us/library/security/MS16-142" target="_blank">MS16-142</a></td>
    <td><a href="https://support.microsoft.com/en-us/kb/3198467" target="_blank">3198467</a></td>
    <td>Cumulative Security Update for Internet Explorer</td>
    <td>11/8/2016</td>
  </tr>
  <tr>
    <td><a href="https://technet.microsoft.com/en-us/library/security/MS16-141" target="_blank">MS16-141</a></td>
    <td><a href="https://support.microsoft.com/en-us/kb/3202790" target="_blank">3202790</a></td>
    <td>Security Update for Adobe Flash Player</td>
    <td>11/8/2016</td>
 </tr>
</tbody>
</table>

However, the page itself never loads this data in.

Is what I'm even doing possible? And if so, what is the step I'm missing here?

Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
Chad D
  • 299
  • 8
  • 21
  • What happens if you remove your `DataTable` call? Because this sure works for me without it. I don't have jquery, and this isn't a [mcve], but I'm guessing that's where your problem is. Do you see any issues in firebug/chrome console? – Wayne Werner Nov 21 '16 at 23:33
  • I'm assuming you're talking about the '$('#bulletin_table').DataTable();' portion? I removed that but no change on my end. I tried to get my code into jsfiddle but I'm not sure flask really works there :\ Thanks for your response! – Chad D Nov 22 '16 at 03:27
  • Is that your full HTML result? Because I'd expect to see ` ....`. I mean, your browser *should* be rendering your page anyway, but apparently it's not, which makes me wonder/suspect that your issue is actually with what you're returning via `render_template`. – Wayne Werner Nov 22 '16 at 14:23
  • Does this answer your question? [Show HTML table with Flask and render\_template](https://stackoverflow.com/questions/53466062/show-html-table-with-flask-and-render-template) – Zaffer Jun 13 '21 at 20:54

1 Answers1

2

Based on the information above I think you need to either:

  1. submit the actual form data to the Flask backend, or
  2. modify your async call to modify the actual DOM table

Currently your $.getJSON call is calling your Flask function but you haven't included a method to update the actual HTML page. It's just returning a set of HTML to the browser but not telling the browser to do anything with it. The server side script will run on a GET or POST request, but making this request via XHR does nothing to tell your browser to refresh. The JQuery $.getJSON() call is a way to asynchronously get data from the Flask backend, but to use this functionality you also need to handle the resulting DOM manipulation in Javascript (e.g. to do this without the page reloading).

DataTables is a great tool. I'm going to ignore it in the first 2 scenarios here and explain how you'd implement this in my third.

So, I think you have 3 options:

1) Have you form POST the data to your Flask application, in this case you'd need to change your view function to accept both GET and POST responses:

@application.route("/ms_bulletins", methods=['GET', 'POST'])

And change your form action to

<form method="POST" action="">

Then remove the Javascript function entirely. This should work, the page will reload on each call.

2) To go the async route would require adding a second Flask function:

from Flask import jsonify

@application.route("/_get_bulletins")
def _get_bulletins():
    date = request.args.get('date', None, type=str)
    session = Session()
    data = session.query(MsBulletins).filter(MsBulletins.date==date)
    bulletins = data.all()
    return jsonify(bulletins)

Then, modify your javascript function to incorporate the results into the DOM:

function dateClick(date){
    var dropdown = document.getElementById("date");
    var date = dropdown.options[dropdown.selectedIndex].value;
    var table = $('#bulletin_table').find('tbody');
    table.empty();
    console.log( "Date: " + date );
    $.getJSON('{{ url_for('._get_bulletins') }}', date, 
        function (data ){
            console.log(data);
            for (var x = 0; x < data.length; x++) {
                var row = $('<tr>');
                row.append($('<td>').text(data[x][0]));
                row.append($('<td>').text(data[x][1]));
                table.append(row);
            }
    });
}

3) Go the Async route and use DataTables. To use this I would recommend passing a more detailed configuration to the DataTables API. It's well documented and powerful. The core functionality to get this to work would be.

Store the DataTable reference in a variable.

var dt = $('#bulletin_table').DataTable();

Change your javascript function to manipulate the DataTable, not the DOM:

function dateClick(date){
    var dropdown = document.getElementById("date");
    var date = dropdown.options[dropdown.selectedIndex].value;
    console.log( "Date: " + date );
    $.getJSON('{{ url_for('._get_bulletins') }}', date, 
        function (data ){
            console.log(data);
            dt.empty();
            dt.rows.add(data).draw();
    });
}
abigperson
  • 5,252
  • 3
  • 22
  • 25
  • I tried each of the examples you gave above, and still no luck unfortunately :( - That being said, this did give me a lot to read and went in depth with the explanation. Using 3.), I'm still seeing the data in the source of the page, but the page never reloads. I tried to use jsfiddle to give an example but I'm not sure python works there. – Chad D Nov 22 '16 at 03:25
  • So, I use SQLAlchemy which returns object style result sets. When I define my DataTables objects I pass it a configuration object to tell it about the data source, how to render, etc. Ultimately you're probably going to have an easier time trying to debug this without the DataTables stuff in there. E.g. getting Flask to communicate with the client asynchronously. Does that part make sense? – abigperson Nov 22 '16 at 13:54
  • It does make sense, and I'm going to spend some time with it here this morning. When I went with number 2, I actually got an error telling me that it could not jsonify my results, so I'll need to look at that as well. Its so bizarre that I can see in the source that its working but the page just does not reload. Thanks for your help! – Chad D Nov 22 '16 at 14:19
  • This is a really powerful combination of tools that I'm a big fan of. Good luck working through everything. Root cause analysis when you're working with 4 or 5 different languages can be tough. I fixed some javascript in my option 2 example...maybe that was the issue there. Option 1 should also work with the GET method. I define my forms as `
    ` (class because I use bootstrap)
    – abigperson Nov 22 '16 at 14:38