I used Arduino to send sensor data to a Raspberry pi, (such as temperature, humidity, smoke level, etc.) Raspberry pi stored them and committed to MySQL database. I am now displaying the dataset on a web server using Flask.
In my python code, I have access to the database.
def getTemps():
cursor = dbConn.cursor()
result = cursor.execute(sql)
if result > 0:
tempDetails = curcor.fetchall()
return render_template("temp.html", tempDetails=tempDetails)
Now, I am able to display them in a table on html like below
{% for data in tempDetails %}
<tr>
<td>{{ data[0] }}</td>
<td>{{ data[1] }}</td>
<td>{{ data[2] }}</td>
<td>{{ data[3] }}</td>
</tr>
{% endfor %}
p.s: data[0] is id, data[1] is temperature, data[2] is humidity, data[3] is timestamp.
However, I wanted to put them in a graph too, I tried to use Chart.js, but I am not sure how to put my list of temperature (and or humidity) in the dataset.
var ctx = document.getElementById('myChart').getContext('2d');
var chart = new Chart(ctx, {
// The type of chart we want to create
type: 'line',
// The data for our dataset
data: {
labels: [/* I want to display each timestamp*/],
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: [/* in here should i use the {% for data in tempDetails %} for loop again?*/]
}]
},
// Configuration options go here
options: {}
});
Any help would be appreciated!!! Cheers!
Maybe this can help pass the data from flask to js: How can I pass data from Flask to JavaScript in a template?
Quote:
" You can use {{ variable }} anywhere in your template, not just in the HTML part. So this should work:
<html>
<head>
<script>
var someJavaScriptVar = '{{ geocode[1] }}';
</script>
</head>
<body>
<p>Hello World</p>
<button onclick="alert('Geocode: {{ geocode[0] }} ' + someJavaScriptVar)" />
</body>
</html>
"
Related
I have a flask route rendering a template (method1_result.html) containing a dataframe table. Clicking the table row will send the cell value to another flask route to render a new template (method2_result.html). This last operation is showing a new result relevant to the Cell Clicked but the new html page (method1_result.html) is displaying the result twice.
main.py
#app.route("/method1",methods=['POST', 'GET'])
def method1():
'
'
return render_template('method1_result.html')
#app.route("/method2",methods=['POST', 'GET'])
def method2():
if request.method == 'POST':
# get info here to render page!
.
.
return render_template('method2_result.html',var1=var1)
method1.html
{% block content %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<br/>
<h3 align="center" style="color:blue" style="font-family:verdana">Method1 Result</h3>
<br/>
<div class="container-fluid">
<table id="table" class="table table-striped table-bordered" style="width: 100%">
<thread>
<tr>
{% for header in table[0].keys() %}
<th>{{header}}</th>
{% endfor %}
</tr>
</thread>
<tbody>
{% for row in table %}
<tr class='clickable-row'>
<td>{{row['Field1']}}</td>
<td>{{row['Field2']}}</td>
<td>{{row['Field3']}}</td>
<td>{{row['Field4']}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script>
highlight_row();
function highlight_row(var1='') {
var table = document.getElementById('table');
var cells = table.getElementsByTagName('td');
for (var i = 0; i < cells.length; i++) {
// Take each cell
var cell = cells[i];
// do something on onclick event for cell
cell.onclick = function () {
// Get the row id where the cell exists
var rowId = this.parentNode.rowIndex;
var rowsNotSelected = table.getElementsByTagName('tr');
for (var row = 0; row < rowsNotSelected.length; row++) {
rowsNotSelected[row].style.backgroundColor = "";
rowsNotSelected[row].classList.remove('selected');
}
var rowSelected = table.getElementsByTagName('tr')[rowId];
rowSelected.style.backgroundColor = "yellow";
rowSelected.className += " selected";
msg = 'The Failure Message : ' + rowSelected.cells[6].innerHTML;
var var1 = rowSelected.cells[3].innerHTML;
$.ajax({
url:"/method2",
method:"POST",
data:{var1:var1},
success:function(data)
{
$('tbody').html(data);
$('tbody').append(data.htmlresponse);
},
})
}
}
}
</script>
{% endblock %}
method2.html
{% block content %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<br/>
<h4 align="center" style="color:blue" style="font-family:verdana">Method2 result</h4><br/>
<br/>
<table id="table" class="table table-striped table-bordered table-hover" style="width: 100%">
<thread>
<tr>
{% for header in table1[0].keys() %}
<th>{{header}}</th>
{% endfor %}
</tr>
</thread>
<tbody>
{% for row in table1 %}
<tr>
<td>{{row['Start']}}</td>
<td>{{row['OperationID']}}</td>
<td style="color:red">{{row['Failure Message1']}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Not sure what Am doing wrong ?
In your code, you are doing an asynchronous call (ajax). An asynchronous call does not reload a page. This means that user is still on method1_result.html when the result (i.e. method2_result.html) comes in.
Your code also says to simply append the entire method2_result.html to method1_result.html page. In fact, you do it twice because you have
// First you append the entire response i.e. the entire method2 result
$('tbody').html(data);
// Then you try to do it a second time but just the 'htmlresponse' (not sure where you are getting this fro
$('tbody').append(data.htmlresponse);
If you want to replace the entire page (method1_result) with the entire contents of method2_result, then you don't need an Ajax call. Replace that ajax call with code that creates a form, creates an input element with the values of the data that you want to pass to the server, and then submits the form. Since this is not an ajax call, it will load a new page which is method2.html
var form = document.createElement("form");
form.method = "POST";
form.action = "/method2";
var elem = document.createElement("input");
elem.name= var1;
elem.value= var1;
form.appendChild(elem1);
document.body.appendChild(form);
form.submit();
If you don't want to replace the entire page (maybe you want to replace the body of the existing page with the new page), then you can't just do append which means to add to the existing content, unless you have first cleared the existing content. You have 2 options
Option 1
$('tbody').empty() // Remove the current contents
$('tbody').append(data); // Add the new contents
Option 2
$('tbody').html(data)
I am facing an issue while accessing the contents in html file of the python list declared in Flask code. Here is the brief details.
FLASK code:
class TestSuiteForm(FlaskForm):
TestCases = ['Check Red LED Status', 'Check Red2 LED Status', 'Check Red3 LED Status']
NumOfTestCases = len(TestCases)
#app.route("/offlineTest")
def offlineTest():
form = TestSuiteForm()
return render_template("offlineTest.html", title='Offline Testing', form=form)
if __name__ == "__main__":
app.run(debug=True)
HTML code: Adding snippet which have the issue.
<p >
<h4>Test Cases2</h4>
<table id="testcaseTable2" style="width:50%"> </table>
</p>
<script language="javascript" type="text/javascript">
var text = "";
text = "<tr> <th>Sr num </th> <th>Test Case Name </th> <th>Select/Deselect</th> </tr>"
for (var i = 0; i < {{form.NumOfTestCases}}; i++)
{
text += '<tr>'
text += '<td>' + [i+1] + '</td>'
text += '<td>"' + {{form.TestCases[i]}} + '"</td>' //Not working; output NaN
text += '<td><input type="checkbox"/> </td> </tr>'
}
document.getElementById("testcaseTable2").innerHTML = text
</script>
I am not able to access the {{form.TestCases[i]}} data, but if I hardcode i with 0,1,2 it gives proper data.
Please let me know the problem. I beleive it is happening due to string concatenation but not sure how to solve it. I am really new to web page design so pardon me if its a stupid mistake.
Use a Jinja for loop to build the table.
<table id="testcaseTable2" style="width:50%">
{% for test_case in form.TestCases %}
<tr>
<td>{{ loop.index + 1 }}</td>
<td>{{ test_case }}</td>
<td><input type="checkbox"/> </td>
</tr>
{% endfor %}
</table>
I'm being trying to put data into a table in flask but its creating a new row for each character for some reason instead of just putting the full string into the row.
code:
#app.route('/')
def logs():
output = ''
try:
conn = redis.StrictRedis(host='redis', port=6379)
for key in conn.scan_iter("log.g*"):
value = str(conn.get(key))
output += "str(key)+ '--' + value"
return render_template('view.html', data=output)
table code:
<table>
{% for row in data %}
<tr>
{% for value in row %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Well, the thing is that your output / data is absolutely unstructured - it is just one big string. You want to make for example a list:
output = []
conn = redis.StrictRedis(host='redis', port=6379)
for key in conn.scan_iter("log.g*"):
value = str(conn.get(key))
output.append("str(key)+ '--' + value")
return render_template('view.html', data=output)
(In your code your return statement is inside the cycle, which means that the cycle will run just once.
The code above will create a list and then the template:
<table>
{% for value in data %}
<tr>
<td>{{ value }}</td>
</tr>
{% endfor %}
</table>
will print each list member into a table cell. Aside of that, we cannot tell what you want to have in one table row.
Dear community members,
I really hope that you will help me to add a Bar Graph to my Django project.
1. I have a Database with around 1000 items.
2. I need to be able visualise a 3 month sales for each item when needed.
Not sure what is a correct approach.
here is my models.py:
from django.db import models
from math import *
from decimal import *
class Itemslist(models.Model):
item_n = models.CharField(max_length=200)
sales_this_month = models.DecimalField(blank=True, null=True, max_digits=19,
decimal_places=0)
saleslm = models.DecimalField(blank=True, null=True, max_digits=19, decimal_places=0)
sales2m = models.DecimalField(blank=True, null=True, max_digits=19, decimal_places=0)
sales3m = models.DecimalField(blank=True, null=True, max_digits=19, decimal_places=0)
def __str__(self):
return self.item_n
here is my views.py file, that as an experiment I have created, using the last solution provided:
def charts(request):
charts = Itemslist.objects \
.values('saleslm') \
.annotate(lm=Count('saleslm')) \
.annotate(m2=Count('sales2m')) \
.annotate(3m3=Count('sales3m')) \
.order_by('saleslm')
return render(request, 'charts.html', {'charts': charts})
As you can see, this is not a solution I need, I was just trying to come up with at least something , and eaven that has shown me the graph with the same values.
here is my hmtl code:
{% extends 'base.html' %}
{% block js %}
{% if user.is_authenticated %}
{% load loads_extra %}
{% load static %}
<br>
<p>Logged in user: {{ user.username }}</p>
<br>
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container"></div>
{
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Sales previous 3 months'
},
xAxis: {
categories: ['sales']
},
series: [{
name: '3mBack',
data: [ {% for entry in charts %}{{ entry.m3 }}{% endfor %} ]
}, {
name: '2mBack',
data: [ {% for entry in charts %}{{ entry.m2 }}{% endfor %} ]
}, {
name: 'Lmonth',
data: [ {% for entry in charts %}{{ entry.lm }}{% endfor %} ]
}, ]
});
</script>
</body>
</html>
{% endif %}
{% endblock %}
<!-- charting tutorial to follow : https://simpleisbetterthancomplex.com/tutorial/2018/04/03/how-to-integrate-highcharts-js-with-django.html -->
I have to create a request button for charts and then chart has to be generated with a right parameters.
Have looked at this question:
Displaying multiple bar graph in django
Also have searched through this solution
https://code.djangoproject.com/wiki/Charts
And looked at this article
https://simpleisbetterthancomplex.com/tutorial/2018/04/03/how-to-integrate-highcharts-js-with-django.html
The last article was the clearest one, and you can see, that I have just copy pasted the solution from there, with small changes.
here is a script that I have placed in my base.html file:
<script src="https://code.highcharts.com/highcharts.src.js"></script>
and that's the chart that I have finally got displayed:
But still can’t find how to deal with it in my situation.
All this solutions, as far as I can see, are showing how to implement charting to one array, or to sum or self generated array. But I want to be able to chose, when to show a graph and for which Item.
the button is placed on this html file:
{% extends 'base.html' %}
{% block js %}
{% if user.is_authenticated %}
{% load loads_extra %}
{% load static %}
<br>
<p>Logged in user: {{ user.username }}</p>
<br>
<body>
<table id="example" class="table table-striped table-bordered dt-responsive nowrap" style="width:100%">
<thead>
<tr>
<th>SUP:</th>
<th>Item N.:</th>
<th>SKU</th>
<th>Description</th>
<th>3mBack</th>
<th>2mBack</th>
<th>Lmonth</th>
<th>CMonth</th>
<th>Nmonth</th>
<th>N2Month</th>
<th>N3month</th>
<th></th>
</tr>
</thead>
<tbody>
{% for records in sorted %}
<tr>
<td>{{ records.sup }}</td>
<td>{{ records.item_n }}</td>
<td>{{ records.sku }}</td>
<td>{{ records.description }}</td>
<td>{{ records.sales3m }}</td>
<td>{{ records.sales2m }}</td>
<td>{{ records.saleslm }}</td>
<td>{{ records.sales_this_month }}</td>
<td>{{ records.m1predicted }}</td>
<td>{{ records.m2predicted }}</td>
<td>{{ records.m3predicted }}</td>
<td>
Edit
</td>
</tr>
{% endfor %}
</tbody>
</table>
<script>
$(document).ready(function() {
var table = $('#example').DataTable( {
fixedColumns: true,
lengthChange: true,
buttons: [ 'copy', 'excel', 'csv', 'pdf', 'colvis' ]
} );
table.buttons().container()
.appendTo( '#example_wrapper .col-md-6:eq(0)' );
} );
</script>
</body>
</html>
{% endif %}
<div></div>
{% endblock js %}
This is my first question in this community, so if something is not clear, please help me to correct it in a right way.
waiting for any helpful answers!!!
Good News community.
I have been offered a very good solution, and now my question is completely sorted.
At least for my needs, this is a perfect solution.
instead of highcharts, I have been offered to use d3js.org, which is absolutely fine for me.
The logic behind a code is that you request data from the values displayed, hopefully it will be clear, when you will go through the code with comments.
as it's a django project, here's the heading of my html file:
{% extends 'base.html' %}
{% block js %}
{% if user.is_authenticated %}
{% load loads_extra %}
{% load static %}
<br>
<p>Logged in user: {{ user.username }}</p>
<br>
here is a part of my html code that displays table/value:
<table id="example" class="table table-striped table-bordered dt-responsive nowrap" style="width:100%">
<thead>
<tr>
<th>SUP:</th>
<th>Item N.:</th>
<th>SKU</th>
<th>Description</th>
<th>6mBack</th>
<th>5mBack</th>
<th>4mBack</th>
<th>3mBack</th>
<th>2mBack</th>
<th>Lmonth</th>
<th>CMonth</th>
<th>Nmonth</th>
<th>N2Month</th>
<th>N3month</th>
<th>AVGrowth</th>
<th></th>
<!-- This is header for new button to draw the Bar Charts -->
<th></th>
</tr>
</thead>
<tbody>
{% for records in sorted %}
<tr>
<td>{{ records.sup }}</td>
<td>{{ records.item_n }}</td>
<td>{{ records.sku }}</td>
<td>{{ records.description }}</td>
<td>{{ records.sales6m }}</td>
<td>{{ records.sales5m }}</td>
<td>{{ records.sales4m }}</td>
<td>{{ records.sales3m }}</td>
<td>{{ records.sales2m }}</td>
<td>{{ records.saleslm }}</td>
<td>{{ records.sales_this_month }}</td>
<td>{{ records.m1predicted }}</td>
<td>{{ records.m2predicted }}</td>
<td>{{ records.m3predicted }}</td>
<td>{{ records.avgrowths }}</td>
<td>
Edit
</td>
<!-- Add new button for drawing Bar Charts -->
<td>
<button class="btn btn-secondary" onclick="draw_chart(this)" data-toggle="modal" data-target="#myModal">Chart</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
please read comments to understand what is what.
and here is the rest of html, that is generating a chart on a topup window,by pressing a button in front of an array that has to be visualised:
<!-- Modal which Bar Chart will be placed -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog" style="max-width: 900px !important">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<div class="modal-body">
<!-- <svg> element which will contains the Bar Chart -->
<svg width="1000" height="500"></svg>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Insert D3.js Library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
$(document).ready(function() {
var table = $('#example').DataTable( {
fixedColumns: true,
lengthChange: true,
buttons: [ 'copy', 'excel', 'csv', 'pdf', 'colvis' ]
} );
table.buttons().container()
.appendTo( '#example_wrapper .col-md-6:eq(0)' );
} );
// Main functions for Drawing Bar chart using D3.js
function draw_chart(item){
// `item` is the current clicked button element
// `row_ele` is the parent <tr> element of the current clicked button element
row_ele = item.closest('tr');
// Get the value from the <td> element using nth-child()
val_6mBack = row_ele.querySelector("td:nth-child(5)");
val_5mBack = row_ele.querySelector("td:nth-child(6)");
val_4mBack = row_ele.querySelector("td:nth-child(7)");
val_3mBack = row_ele.querySelector("td:nth-child(8)");
val_2mBack = row_ele.querySelector("td:nth-child(9)");
val_Lmonth = row_ele.querySelector("td:nth-child(10)");
val_CMonth = row_ele.querySelector("td:nth-child(11)");
val_Nmonth = row_ele.querySelector("td:nth-child(12)");
val_N2Month = row_ele.querySelector("td:nth-child(13)");
val_N3month = row_ele.querySelector("td:nth-child(14)");
// `data` is variable which store the data for Bar Charts
data = []
// Pushing data as key/value type objects into the `data` variable
data.push({'label':'6mBack', 'value': val_6mBack.innerHTML})
data.push({'label':'5mBack', 'value': val_5mBack.innerHTML})
data.push({'label':'4mBack', 'value': val_4mBack.innerHTML})
data.push({'label':'3mBack', 'value': val_3mBack.innerHTML})
data.push({'label':'2mBack', 'value': val_2mBack.innerHTML})
data.push({'label':'Lmonth', 'value': val_Lmonth.innerHTML})
data.push({'label':'CMonth', 'value': val_CMonth.innerHTML})
data.push({'label':'Nmonth', 'value': val_Nmonth.innerHTML})
data.push({'label':'N2Month', 'value': val_N2Month.innerHTML})
data.push({'label':'N3month', 'value': val_N3month.innerHTML})
// Set <svg> element's width and height
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
// Remove the old contents of the <svg> element
svg.selectAll('*').remove()
// Initialize X-axis and Y-axis for Bar Chart
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
// Set all group which is placed in the <svg>element
// transform to (50,100) on <svg> area , margint in svg has been changed to 300 and instead 50/100, changed to 100/300, but then back.
var g = svg.append("g")
.attr("transform", "translate(" + 50 + "," + 100 + ")");
xScale.domain(data.map(function(d) { return d.label; }));
// If all values of data will be zero, we will fix the range of the Y-axis
if(d3.max(data, function(d) { return d.value; }) == 0){
yScale.domain([0, 10]);
}else{
// If all is not zero, we will set Y-axis from 0 to maximum value.
yScale.domain([0, d3.max(data, function(d) { return Number(d.value); })]);
}
// Set X- axis
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
// Set Y-axis using ticket
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return d;
}).ticks(10));
console.log(data)
// Draw Bar Chart using <rect> element by data which is stored in the `data` variable
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.label); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
.style('fill','#899da6');
}
</script>
</body>
{% endif %}
{% endblock js %}
so finally here is how my html looks like live:
and here is a graph, that I get by pressing chart button:
in order to help community, please ask questions, and please add/correct if there is a better way.
Thank you very much for your help and attention!!!
views.py
You have an error on annotate(3m3 its cannot start with 3 here. You also have same function name and variable name def charts and charts inside. Python might be smart enough to figure it out but try to avoid doing this. I am also against naming variables saleslm and sales2m be explicit here.
def charts(request):
return render(request, 'charts.html')
def charts_ajax(request):
charts_data = Itemslist.objects \
.values('saleslm') \
.annotate(lm=Count('saleslm')) \
.annotate(m2=Count('sales2m')) \
.annotate(m3=Count('sales3m')) \
.order_by('saleslm')
return JsonResponse({'charts': list(charts_data)})
url.py
path('charts/', views.charts, name='charts_home'),
path('charts_ajax/', views.charts_ajax, name='render_charts_ajax')
html
You have multiple issues in your html file.
</html>
{% endif %}
<div></div>
{% endblock js %}
You have div after the html has ended. You should not have anything after html ends.
Few other organizational issues here. I usually would have a block content and then block js while you have everything inside block js. I would clean those. Now that you added datables also. You can add buttons as mentioned here. https://datatables.net/extensions/buttons/examples/initialisation/custom.html
but if i were you i would try to make it work with simple table and move on to datables.
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container">
</div>
<button id="render_chart" type="button">Render Chart</button>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
$(document).ready(function(){
$('#render_chart').on('click', function(e){
$.ajax({
url: "{% url 'render_charts_ajax'%}",
type: "GET",
dataType: "json",
success: function (data) {
console.log(data.charts[0].saleslm)
Highcharts.chart('container',{
chart:{
type:'bar'
},
title:{
text:"Sales Last Month"
},
series:[{
name:"Sales LM",
data:[parseInt(data.charts[0].saleslm)]
}]
})
}
})
})
});
</script>
</body>
</html>
Your json might need some processing before the chart comes out right.
I'm building a Flask app that includes a table where users can change a cell and have those changes 'applied' e.g. updated in the database.
Pictures bellow
I'm having issues getting flask to retrieve this data that has been submitted via form.
The number of rows are dynamic and dependent on the number of Id's in a list.
Here is my html -- sorry for the clunkiness, I am still learning.
<tbody>
<form method="post" name="productCost">
{% for n in range(name_list | length) %}
<tr>
<th scope="row" >{{name_list[n]}}</th>
<td>{{ID_list[n]}}</td>
<td id="cost{{n}}">${{cost_list[n]}}</td>
<td>---</td>
<td id="changes{{n}}"><button onclick="costChanges{{n}}()">Edit</button></td>
</tr>
<script>
function costChanges{{n}}() {
document.getElementById("cost{{n}}").innerHTML = "<input placeholder='{{cost_list[n]}}' name={{ID_list[n]}}>";
document.getElementById("changes{{n}}").innerHTML = "<button onclick='applyChanges{{n}}()' type='submit'>Apply</button><button onclick='cancelChanges{{n}}()'>Cancel</button>";
}
function applyChanges{{n}}() {
docuemnt.getElementById("cost{{n}}").innerHTML = document.forms["productCost"]["{{ID_list[n]}}"]
}
function cancelChanges{{n}}() {
document.getElementById("cost{{n}}").innerHTML = "{{cost_list[n]}}";
document.getElementById("changes{{n}}").innerHTML = "<button onclick='costChanges{{n}}()'>Edit</button>";
}
</script>
{%endfor%}
</form>
</tbody>
Here is my python/flask code:
app.route('/expenses', methods=['GET', 'POST'])
def expenses():
if 'email' not in session:
return redirect(url_for('login_now'))
list_of_product_dicts = get_name_id()
name_list = []
asin_list =[]
cost_list=[]
for p in list_of_product_dicts:
name_list.append(p['name'])
id_list.append(p['id'])
cost = get_landing_cost(p['id'])
cost_list.append(cost)
if request.method == 'POST':
print(request.form['name'])
return flask.render_template('expenses.html', name_list = name_list, id_list=id_list,
cost_list=cost_list)
I need python to recognize the change that has been made and store it in a variable. This is for the purpose of updating it in a database-- but I do not need assistance with the database code. I only need help getting python to grab the cell that has been changed and recognize what row it was in.
It is a bit simpler to use jquery with ajax. The latter enables you to dynamically update the table by communicating with a backend script to update your database.
First, create the HTML script (the code below creates a different table layout for this example, however, you can substitute your own, based on the one below). The jquery cleanly handles the Apply, Cancel, and Edit button functionalities, and communicates with the backend via ajax:
tables.html:
<html>
<body>
<table>
<tr>
<th>ID</th>
<th>Cost</th>
<th>Effective Date</th>
<th>Make Changes</th>
</tr>
{%for row in data%}
<tr>
<td>{{row.id}}</td>
<td class='cost{{row.rowid}}'>{{row.price}}</td>
<td>{{row.date}}</td>
<td>
<div id='options{{row.rowid}}'>
<button id='mutate{{row.rowid}}'>Edit</button>
</div>
<td>
</tr>
{%endfor%}
</table>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$("div[id^='options']").on('click', 'button', function(){
var the_id = this.id.match('\\d+');
var the_type = this.id.match('[a-zA-Z]+');
if (the_type == "mutate"){
$('#options'+the_id).html('<button id="cancel'+the_id+'">Cancel</button>\n<button id="apply'+the_id+'">Apply</button>');
var current_cost = $('.cost'+the_id).text();
$('.cost'+the_id).html('\n<input type="text" class="newval'+the_id+'" value="'+current_cost+'">')
}
else if (the_type == 'cancel'){
$('#options'+the_id).html('<button id="mutate'+the_id+'">Edit</button>');
}
else{
var value = $(".newval"+the_id).val();
$.ajax({
url: "/update_cell",
type: "get",
data: {newval: value, rowid:the_id},
success: function(response) {
$('.cost'+the_id).html(value);
$('#options'+the_id).html('<button id="mutate'+the_id+'">Edit</button>');
},
error: function(xhr) {
//Do Something to handle error
}
});
}
});
});
</script>
</html>
Then, create the app and routes:
import flask, sqlite3, collections
app = flask.Flask(__name__)
#app.route('/', methods=['GET'])
def home():
dummy_data = [['hvVlNnAW', '$6.00', '--'], ['UqBzqLho', '$10.00', '--'], ['tuEdqldI', '$3.00', '--'], ['MIHLFWDS', '$1.00', '--'], ['rUnjpHiJ', '$8.00', '--'], ['lHVHxgZF', '$1.00', '--'], ['nFfaHkHj', '$3.00', '--'], ['rRWqXqVh', '$8.00', '--'], ['rdzCRozr', '$4.00', '--'], ['MGojGbtW', '$9.00', '--']]
#substitute dummy_data with a database query
product = collections.namedtuple('product', ['id', 'price', 'date', 'rowid'])
return flask.render_template('tables.html', data = [product(*a, i) for i, a in enumerate(dummy_data, 1)])
#app.route('/update_cell')
def update_row():
new_val = flask.request.args.get('newval')
prod_id = int(flask.request.args.get('rowid[]'))
#commit new_val to database
return flask.jsonify({'success':True})
Now, in '/update_cell', you have the new price value, and the product row id, which tells you which sqlite row's price you are to update. Note that in home, the row id, which is garnered by enumerate is vital to the application as it enables the jquery to know what table and sqlite row to update.
Demo: