Flask, AJAX, & Bootstrap Tables

Joseph Dougal
Nov 2 · 4 min read

This article shows you how to setup a Flask web app that loads a JSON object into an HTML table. It also re-creates the table by making an AJAX POST request to a Flask endpoint.

Quick version

For everyone that likes to see the results asap follow these steps to run the project locally.

Step by step

If you don’t want to download the repo you can manually create the folders/files in the project structure below and copy/paste the code.

Overview

When you run the Flask web app and go to http://127.0.0.1:5000/ in your browser the @app.route(‘/’) renders index.html. We pass the first JSON object ‘data_set_1’ to the Jinja2 variable ‘data’. In index.html we use Jinja2 expressions to create a for loop and iterate over the JSON object to build the table.

Project structure

Python

app.py

from flask import Flask, render_template, jsonify, request
import pandas as pd
import json
import os

app = Flask(__name__)

app.config['JSON_SORT_KEYS'] = False

project_dir = os.path.dirname(os.path.abspath(__file__))
my_files = r'/static/data/'
file_dir = project_dir + my_files


@app.route('/')
def index():

json_file = file_dir + r'data_set_1.json'

with open(json_file) as f:
js_object = json.load(f)
df = pd.read_json(json.dumps(js_object))
return render_template('index.html', data=df)


@app.route('/get_data', methods=['POST'])
def get_data_function():

user = request.form['user']

if user == 'two':

json_file = file_dir + r'data_set_2.json'

with open(json_file) as f:
js_object = json.load(f)
return jsonify(js_object)

else:
json_file = file_dir + r'data_set_1.json'

with open(json_file) as f:
js_object = json.load(f)
return jsonify(js_object)


if __name__ == '__main__':
app.run(debug=True)

HTML

index.html

<!DOCTYPE html>
<html lang="en">
<head>

<meta charset="utf-8" />
<title>AJAX Example</title>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"></script>
<script src="{{ url_for('static', filename='js/ajax_table_example.js') }}"></script>

</head>
<body>


<h2> [Example] </h2>


<div class="container">

<form enctype="multipart/form-data" action='download' method="POST">
<div class="form-row mb-3">
<div class="input-group col-sm-8">
<div class="input-group-prepend">
<button type="button" id="ajax_data_load" class="btn btn-secondary fixed-button-size">Run</button>
</div>
<select name="user" class="custom-select fixed-button-size">
<option value="one">Option..</option>
<option value="one">One</option>
<option value="two">Two</option>
</select>
</div>
</div>
</form>

<table id="jds-example" class="table">
<thead>
<tr>
{% for col in data.columns %}
<th scope="col"> {{ col }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for key, value in data.iterrows() %}
<tr>
{% for k in value %}
<td scope="row">{{ k }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>

</div>


</body>
</html>

Styling

main.css

table {
table-layout: responsive-sm;
}

thead th {
background-color: #FFA500;
color: white;
}

td {
white-space: normal !important;
word-wrap: break-word;
}

.fixed-button-size {
width: 100px !important
}

JSON

data_set_1.json

[{
"name": "John Doe",
"position": "Sales",
"salary": "$100,000",
"start_date": "2015",
"office": "New York",
"extn": "5421"
},
{
"name": "Larry Doe",
"position": "Trader",
"salary": "$100,000",
"start_date": "2018",
"office": "Tokyo",
"extn": "2154"
}]

data_set_2.json

[{
"name": "Joe Doe",
"position": "Technology",
"salary": "$100,000",
"start_date": "2015",
"office": "Miami",
"extn": "4512"
},
{
"name": "Ashley Doe",
"position": "Fashion",
"salary": "$100,000",
"start_date": "2015",
"office": "Alaska",
"extn": "1245"
}]

If there were no errors, you’ll see this in your browser:

AJAX

To update an element in the DOM we do an asynchronous call with AJAX to our flask endpoint @app.route(‘/get_data’, methods=[‘POST’]) to get a new JSON object. We then build a new HTML table with the same CSS styling. This gives our web app a modern day feel.

$(document).ready(function(){
$('#ajax_data_load').click(function(){
clicked = $(this).attr('name');
$.ajax({
url: '/get_data',
dataSrc: 'data',
type: 'POST',
dataType: 'json',
data: $('form').serialize(),
success: function(data){
console.log('Success Hit');
console.log(data);
$('#jds-example').html('');

var column_data = '';
column_data += '<tr>';

for (var key in data[0]){
column_data += '<th>' + key + '</th>'
};

column_data += '</tr>';
$('#jds-example').append(column_data),
$('th').css({'background-color':'#FFA500', 'color': 'white'});

var row_data = '';
for (var arr in data){
var obj = data[arr];
row_data += '<tr>';
for (var key in obj){
var value = obj[key];
row_data += '<td>' + value + '</td>';
};
row_data += '</tr>'
};
$('#jds-example').append(row_data);

},
error: function(data){
console.log('Error Hit');
console.log(data);
}
});
});
});

If there are no errors you’ll see this in your browser:

You can also inspect the app in your browser and see the message ‘Success Hit’ we wrote to the console and the object loaded from our flask endpoint and :

    Joseph Dougal

    Written by

    Senior Developer | Trading Technology

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade