Adding two numbers in flask - python

<html>
<head>
<title>Addition</title>
<script>
function display(id_name,result_name){
document.getElementById(result_name).innerHTML = document.getElementById(id_name).value;
}
function calculate(id1,id2,result_id) {
document.getElementById(result_id).innerHTML = parseInt(document.getElementById(id1).value)+parseInt(document.getElementById(id2).value)
}
</script>
</head>
<body>
<div class="input">
Enter 1st Number:
<input type="text" id="input1_text">
<button type="button" onclick="display('input1_text','input1')">Enter 1st Number</button>
<span id="input1"></span>
</div>
<div class="input">
Enter 2nd Number:
<input type="text" id="input2_text">
<button type="button" onclick="display('input2_text','input2')">Enter 2nd Number</button>
<span id="input2"></span>
</div>
<div class="result">
<button type="button" onclick="calculate('input1_text','input2_text','result_value')">Calculate</button>
<span id="result_value"></span>
</div>
</body>
</html>
so in the above code i am not only adding 2 nubmers but also displaying the numbers after pressing the button.so now what i am looking for is to make this using flask framework on pressing buttons their respective functions should be fired up and data should be updated.I have tried using forms so the problem is on clicking button of 1st number to display is whole page is refreshing and i am losing whole data.so now how do i write those functions in python and also making sure that the page should not reload.Also is it possible to reuse the display function based on the paramters rather than hard coding and writing 2 display functions for 2 numbers seperately

Unless you need to use the user inputted values in the backend of your application, you can simply perform the calculations in the front-end:
<html>
<body>
<p>Input first number:</p>
<input type='text' class='first_val'>
<p>Input second number:</p>
<input type='text' class='second_val'>
<div class='results'></div>
<button type='button' class='calculate'>Calculate</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('.calculate').click(function() {
var val1 = parseInt($('.first_val').val());
var val2 = parseInt($('.second_val').val());
var val3 = val1+val2;
$('.results').html('The result is '+val3);
});
});
</script>
</html>
Edit: using Python in the backend, ajax can be utilized:
index.html:
<html>
<body>
<p>Input first number:</p>
<input type='text' class='first_val'>
<p>Input second number:</p>
<input type='text' class='second_val'>
<div class='results'></div>
<button type='button' class='calculate'>Calculate</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js">
</script>
<script>
$(document).ready(function() {
$('.calculate').click(function() {
var val1 = $('.first_val').val();
var val2 = $('.second_val').val();
$.ajax({
url: "/get_numbers",
type: "get",
data: {val1: val1, val2:val2},
success: function(response) {
$(".results").html(response.packet);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
Then, in the Python app:
#app.route('/', methods=['GET', 'POST'])
def home():
return flask.render_template('index.html')
#app.route('/get_numbers')
def get_values():
value1 = flask.request.args.get('val1')
value2 = flask.request.args.get('val2')
return flask.jsonify({'data':f'<p>The result is: {value1+value2}</p>'})

You should build an flask API function: a GET route which accepts the argument from your form and returns a JSON response object.
Then in your HTML when you click the button that should perform an AJAX GET quesry accessing your API route and return the values to that page. This avoids having Flask re-render your page and returns the data directly to your existing client web browser.
To make it easy to manipulate data dynamically in the webpage I suggest a JS framework like angular, react or vue. Personally I prefer vue since it can be self contained, loaded as script into the page and the setup is often minimal. The docs are also very easy and you can easily see how to link form input controls.

Related

Trigger onchange via Selenium from Python

I have a Django webapp displaying a form. One of the fields is a FileField, defined via the Django model of the form:
From models.py:
class Document(models.Model):
...
description = models.CharField(max_length=100, default="")
document = models.FileField(upload_to="documents/", max_length=500)
The document file_field has an onchange ajax function attached that will parse the uploaded filename, check some database stuff depending on it, and populate other fields on the html-page with the results.
From forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ("document",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["customer"] = forms.CharField(initial="", required=True)
self.fields["output_profile"] = forms.CharField(initial="", required=True)
self.fields["document"].widget.attrs[
"onchange"
] = "checkFileFunction(this.value, '/ajax/check_file/')"
From urls.py:
urlpatterns = [
#...
path("ajax/check_file/", views.check_file, name="ajax_check_file")
]
From views.py:
def check_file(request):
full_data = {"my_errors": []}
my_path = pathlib.Path(request.GET.get("file_path").replace("\\", os.sep))
# parse customer ID from file_path
# get data of customer from db
# assemble everything into full_data
return JsonResponse(full_data)
This is the full html page as displayed (copied from Chrome => show source and cleaned up the indentation & whitespaces some):
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link href="/static/css/main.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css"/>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/dataTables.jqueryui.css"/>
<script src="/static/js/jquery.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.20/js/dataTables.jqueryui.js"></script>
<title>
Convert RES FILE
</title>
</head>
<body>
<header id="header">
<section class="top_menu_left">
Login
<a>&nbsp|&nbsp</a>
Logout
<a>&nbsp|&nbsp</a>
Edit User
<a>&nbsp|&nbsp</a>
Register
</section>
<section class="top_menu_right">
About Us
<a>&nbsp|&nbsp</a>
Contact Us
<a>&nbsp|&nbsp</a>
Submit an issue
<a>&nbsp|&nbsp</a>
Documentation
<a>&nbsp|&nbsp</a>
Home
</section>
<div id="topbanner" >
<img src="/static/banner_small.png" alt="" width="100%" height="150"/>
</div>
</header>
<aside id="leftsidebar">
<section class="nav_account">
<h4>Submit a New Request</h3>
<ul>
<li>Get Typing Results</li>
<li>Compare Typing Results</li>
<li>Convert Typing Format</li>
</ul>
</section>
<section class="nav_tools">
<h4>View Your Requests</h3>
<ul>
<li>View My Submissions</li>
</ul>
</section>
</aside>
<section id="main">
<p> </p>
<h2>Convert Typing Results to Format of Choice</h2>
<p> </p>
<h3>Upload a file to our database</h3>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="csrfmiddlewaretoken" value="qqIKcsAynuE35MQ37dvjF5XeIyfcEbHb3wjtgygGZaigQReNxHLQewoDKcEb8Roj">
<div id="div_id_document" class="form-group">
<label for="id_document" class=" requiredField">
Document<span class="asteriskField">*</span>
</label>
<div class="">
<input type="file" name="document" onchange="checkFileFunction(this.value, '/ajax/check_file/')" class="clearablefileinput form-control-file" required id="id_document">
</div>
</div>
<input type="hidden" id="id_description" name="description" value="">
<p> </p>
<div id="div_id_customer" class="form-group">
<label for="id_customer" class=" requiredField">
Customer<span class="asteriskField">*</span>
</label>
<div class="">
<input type="text" name="customer" readonly class="textinput textInput form-control" required id="id_customer">
</div>
</div>
<div id="div_id_output_profile" class="form-group">
<label for="id_output_profile" class=" requiredField">
Output profile<span class="asteriskField">*</span>
</label>
<div class="">
<input type="text" name="output_profile" readonly class="textinput textInput form-control" required id="id_output_profile">
</div>
</div>
<div class="form-group">
<div id="div_id_notify_me" class="form-check">
<input type="checkbox" name="notify_me" style="width:15px;height:15px;" class="checkboxinput form-check-input" id="id_notify_me">
<label for="id_notify_me" class="form-check-label">
Notify me
</label>
</div>
</div>
<p>
<button class="linkbutton" type="submit" id="submit_btn">Convert</button>
<button id="create-book" class="linkbutton" type="button" name="button" style="float: right;">Create an Output Profile</button>
</p>
</form>
<div class="modal fade" tabindex="-1" role="dialog" id="modal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content" id="form-modal-content">
</div>
</div>
</div>
<div class="modal fade" tabindex="-1" role="dialog" id="modal-check-res-file">
<div class="modal-dialog" role="document">
<div class="modal-content" id="form-modal-content-check-res-file">
</div>
</div>
</div>
<script>
var formAjaxSubmit = function(form, modal) {
$(form).on('submit', function (e) {
e.preventDefault();
console.log("submitting...");
var my_val = $("#id_profile_name").val();
var this_val = $("#confirm_save").val();
var res = this_val.split(",");
var this_val_contains_my_val = (res.indexOf(my_val) > -1);
if (this_val_contains_my_val === true) {
var conf = confirm("Are you sure want to overwrite an exsisting profile?");
}else {var conf = true;};
if (conf === true) {
$.ajax({
type: $(this).attr('method'),
url: "/new_customer_profile/",
data: $(this).serialize(),
success: function (xhr, ajaxOptions, thrownError) {
if ( $(xhr).find('.invalid-feedback').length > 0 ) {
$(modal).find('.modal-content').html(xhr);
formAjaxSubmit(form, modal);
} else {
$(modal).find('.modal-content').html(xhr);
}
},
error: function (xhr, ajaxOptions, thrownError) {
}
});
};
});
};
$('#create-book').click(function() {
console.log("hhallo");
$('#form-modal-content').load('/new_customer_profile/', function () {
var iam_alive = document.getElementById("modal");
// check if iam_alive is defined, this is required if a session expired -> in that case the modal is lost and it would redirect to an almost empty page.
if (iam_alive) {
$('#modal').modal('toggle');
formAjaxSubmit('#form-modal-body form', '#modal');
}
// if not iam_alive: redirect to login page
else {
window.location.replace('/accounts/login/');
}
});
});
$('#check-res-file').click(function() {
console.log("hhallo hier unten jetzt");
$('#form-modal-content-check-res-file').load('/check_res_file/', function () {
$('#modal-check-res-file').modal('toggle');
//formAjaxSubmit('#form-modal-body form', '#modal');
});
});
</script>
<script type="text/javascript">
$(document).ready(function() {
var mycell = document.getElementById("create-book");
mycell.style.display = "none";
});
</script>
<script>
function checkFileFunction(myfile, url) {
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/hr/ajax/load-cities/)
data: {"file_path": myfile},
dataType: 'json',
success: function (x) {
if (x.my_errors.length == 0) {
$('#id_customer').val(x.customer_name);
$('#id_output_profile').val(x.customer_profile);
$('#id_description').val(x.customer_file);
}else{
$('#id_customer').val("");
$('#id_customer').val("");
$('#id_output_profile').val("");
alert(x.my_errors);
var showme = function myFunction() {
var mycell = document.getElementById("create-book");
mycell.style.display = "block";
};
showme();
}
},
});
}
</script>
</section>
</body>
</html>
Now, I'm trying to test this with pytest via Selenium.
I can send the file path to the field via send_keys(). However, the onchange event seems not to be triggered. (It does work fine when I select the file manually.)
file_field = self.driver.find_element(By.NAME, "document")
file_field.clear()
file_field.send_keys(str(path/to/myfile))
This will register the file fine and it will be uploaded, but the onchange function never happens.
I have searched and it seems others also have encountered the problem of send_keys not triggering the onchange event. But I have not been able to implement any of the suggested solutions in my Python code. (I have not written the Django code for this app, I'm just the tester and my grasp on Django and javascript is not very good, yet. My native programming language is Python.)
The only solution I understood how to implement was sending a TAB or ENTER afterwards (file_field.send_keys(Keys.TAB)) to change the focus, but that triggers an
selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: File not found
(The file I enterted does exist, the path is fine. I can successfully call .exists() on it.)
Simply selecting a different element after send_keys to shift the focus (i.e., customer_field.click()) does not trigger the onchange function of file_field, either.
How can I trigger an onchange event via Selenium from Python? Or otherwise make sure it is triggered?
First of all, your onchange specification is a bit kludgy and would be preferably specified as:
<input type="file" name="document" onchange="checkFileFunction(this.value, '/ajax/check_file/');">
I am using Selenium with the latest version of Chrome and its ChromeDriver under Windows 10 and have no problems with the onchange event being taken. This can be demonstrated with the following HTML document. If the onchange event is taken, then it should create a new div element with id 'result' that will contain the path of the filename selected:
File test.html
<!doctype html>
<html>
<head>
<title>Test</title>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<script>
function checkFileFunction(value)
{
const div = document.createElement('div');
div.setAttribute('id', 'result');
const content = document.createTextNode(value);
div.appendChild(content);
document.body.appendChild(div);
}
</script>
</head>
<body>
<input type="file" name="document" onchange="checkFileFunction(this.value);">
</body>
</html>
Next we have this simple Selenium program that sends a file path to the file input element and then waits for up to 3 seconds (with the call to driver.implicitly_wait(3)) for an element with an id value of 'result' to be found on the current page and then prints out the text value. This element will only exist if the onchange event occurs:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("headless")
options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(options=options)
try:
# Wait up to 3 seconds for an element to appear
driver.implicitly_wait(3)
driver.get('http://localhost/test.html')
file_field = driver.find_element_by_name("document")
file_field.clear()
file_field.send_keys(r'C:\Util\chromedriver_win32.zip')
result = driver.find_element_by_id('result')
print(result.text)
finally:
driver.quit()
Prints:
C:\Util\chromedriver_win32.zip
Now if your driver is different and that is the reason why the onchange event is not occurring and you do not wish to or cannot switch to the lastest ChromeDriver, then you can manually execute the function specified by the onchange argument. In this version of the HTML file, I have not specified the onchange argument to simulate the situation where specifying it has no effect:
File test.html Version 2
<!doctype html>
<html>
<head>
<title>Test</title>
<meta name=viewport content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<script>
function checkFileFunction(value)
{
const div = document.createElement('div');
div.setAttribute('id', 'result');
const content = document.createTextNode(value);
div.appendChild(content);
document.body.appendChild(div);
}
</script>
</head>
<body>
<input type="file" name="document">
</body>
</html>
And the new Selenium code:
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("headless")
options.add_experimental_option('excludeSwitches', ['enable-logging'])
driver = webdriver.Chrome(options=options)
try:
# Wait up to 3 seconds for an element to appear
driver.implicitly_wait(3)
driver.get('http://localhost/test.html')
file_field = driver.find_element_by_name("document")
file_field.clear()
file_path = r'C:\Util\chromedriver_win32.zip'
file_field.send_keys(file_path)
# Force execution of the onchange event function:
driver.execute_script(f"checkFileFunction('{file_path}');")
result = driver.find_element_by_id('result')
print(result.text)
finally:
driver.quit()
Update
I guess you missed <script src="/static/js/jquery.js"> in the <head> section, which appears to be jQuery. But I would have thought that with this script tag being in the <head> section that jQuery would have to be loaded by time Selenium found the file element. So I confess that your getting a javascript error: $ is not defined is somewhat surprising. I can only suggest now that you try loading it manually as follows as detailed in the code below.
I have re-iterated the 3 things you should try in order in the code below moving on to the next approach if the previous one does not work:
Give jQuery time to load before sending keystrokes to eliminate the $ not defined error.
Manually loading jQuery before sending keystrokes.
Manually executing the checkFileFunction.
# 1. Give jQuery time to load before sending keystrokes:
import time
time.sleep(3)
# 2. if the above sleeping does not work,
# remove the above call to sleep and manually load jQuery.
# Specify where the home page would be loaded from:
document_path = '/home/usr/account/public_html' # for example
jQuery_path = document_path = '/static/js/jQuery.js'
with open(jQuery_path, 'r') as f:
jQuery_js = f.read()
self.driver.execute_script(jQuery_js)
# Send keystrokes:
file_path = str(path/to/myfile)
file_field.send_keys(file_path)
# 3. Execute the following if the onchange doesn't fire by itself:
self.driver.execute_script(f"checkFileFunction('{file_path}', '/ajax/check_file/');")
As it turns out, the actual problem was that my manual test were done on the Django app served via python manage.py runserver. This calls some hidden Django magic, including collecting the statics files (css, jQuery.js etc.) under the hood.
I now learned that, in order to serve a Django app on a proper server, one needs to first call python manage.py collectstatic. This will generate a static folder in the parent directory, which contains all the static files and also an explicit jQuery.js.
Then, when Selenium is run, it will find that static folder and the jQuery.js file therein. And then, everything works as expected, including onchange.
So the problem was, that this parent static folder was missing, which I never saw because serving the website via python manage.py runserver doesn't need it.

Get data to a python variable from a html input?

I am new to python. I need to get data from a html input to a python variable. But I can't find a way to do this. It shows output as None, None, None for output.
It doesn't print out the data too. How can I fix this issue?
This is the code in html file.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type=text/javascript>
$(function() {
$('button#add-income').bind('click', function() {
$.getJSON('/add_income',
function(data) {
//do nothing
});
return false;
});
});
</script>
<form class="add-income-box" method="POST" action="">
<div class="add-income-expences-top-div">
<label>INCOME</label>
</div>
<div class="add-income-expences-label-div">
<label class="add-income-expences-date-lbl">Date : </label>
<label class="add-income-expences-details-lbl">Details : </label>
<label class="add-income-expences-amount-lbl">Amount : </label>
</div>
<div class="add-income-expences-input-div" id="div1">
<input class="add-income-expences-date-input" name="i_date" value="{{request.form.i_date}}">
<input class="add-income-expences-details-input" name="i_details" value="{{request.form.i_details}}">
<input class="add-income-expences-amount-input" name="i_amount" value="{{request.form.i_amount}}">
</div>
<button class="add-income-expences-bottom" id="add-income">ADD</button>
</form>
this is the code in python file
#app.route('/add_income', methods=["GET", "POST"])
def add_income():
i_date = request.form.get('i_date')
i_details = request.form.get('i_details')
i_amount = request.form.get('i_amount')
print(i_date)
print(i_details)
print(i_amount)
return "nothing"
output :-
None
None
None
127.0.0.1 - - [31/May/2020 16:56:07] "GET /add_income HTTP/1.1" 200 -
Change this form tag action attribute
<form class="add-income-box" method="POST" action="{{ url_for('update_project')}}">
In this way, when you click on button it will call the method specified in action attribute.
and if you want to call this API using AJAX then make use of JSON to send data. And if you want to send data using form then remove the Ajax code and change the code as above suggested and click on the button and you will not get the form data.

Getting button name as input

In this example I am assigning a variable (hsname) the school that is selected when sending the form:
<form class="w3-wide" action="/hschool" method="post">
<input list="hschools" name="hsname" autocomplete="off">
<datalist id="hschools">
<option value= "hs1">
<option value= "hs2">
</datalist>
<input type="submit">
</form>
I then use this value in python (with flask) and to get it I use the following (in a separated .py file):
hsname = request.form.get("hsname")
The previous is working fine.
In a similar way, I want to assign to a variable the name of the button that the user is clicking (b1 or b2). The following is not working as there is not variable that records the button that the user clicks.
<form class="w3-wide" action="/subject" method="post">
<button class="button button4" name="b1">b1</button>
<button class="button button4" name="b2">b2</button>
</form>
How can I do it?
I know I could use a radio button here but aesthetically I would rather use a normal button. I know I can use the onclick method but what should I write inside so that it stores a variable that is directly sent to flask?
If you want to send the info to the server as soons as the button is clicked, you can give a name and a value to an html <button> and those will be sent with the form:
<form class="w3-wide" action="/subject" method="post">
<button type="submit" name="clicked_btn" value="b1">b1</button>
<button type="submit" name="clicked_btn" value="b2">b2</button>
</form>
Once one of these buttons is clicked, the form will be submitted and the name and value (e.g. clicked_btn=b1) will be sent to the server (along with any other inputs in the form).
On the other hand, if you want to just remember the state of the button, so that it behaves like a checkbox or like a radio button, only to be sent to the server when a (different) submit button is clicked, then I suggest actually using a checkbox or a radio button and modifying its appearance using CSS to make it look like a button.
You can also use javascript to store some information into a <input type=hidden> input, which will later be sent to the server.
You can create an id for each button that can be captured on the client side send to the server via ajax:
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<input type='text' id='value'>
<button class="button button4" name="b1">b1</button>
<button class="button button4" name="b2">b2</button>
</form>
<div id='user_results'></div>
<script>
$(document).ready(function() {
$('button').click(function(even){
var the_id = event.target.id;
var user_input = $('#value').val();
$.ajax({
url: "/get_user_data",
type: "get",
data: {btn: the_id, result:iuser_input},
success: function(response) {
$('user_results').html(response);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
Then, in your .py file, create a route to receive the data from ajax:
#app.route('/get_user_data')
def get_data():
button_id = flask.request.args.get('btn')
user_input = flask.request.args.get('result')
return '<p>Thanks</p>'
You could put your button in a form with a hidden input and access the value with request.form.get("hsname") or request.POST.get("hsname) depending on how flask implements this (i'm more familiar with django).
Note that you will need 1 form+input per buttons
<form method="POST" action="some_url/">
<input type="hidden" name="hsname" value="the_name_of_the_button">
<button class="button button4" type="submit"></button>
</form>
EDIT: this is html 4.1, for html 5 see zvone's answer.

html form validation combined with flask and python

I have a web app that collects some data in text boxes then sends them to a python script that is running with flask
I have found two ways of submitting the form.
my button manages to collect the data and send it of to python and retires the answer from python but does not validate the inputs
my input tag manages to validate the text buttons but clears the form and does not manage to send the data of to the python script for processing.
I would like to do aspects of both buttons , I would like to validate , send data to python and retired the data.
Any ideas how to combine the function of the input submit and the button into one clickable item that validates and submits?
Any help much appreachiated
{% extends "layout.html" %}
{% block body %}
<script type="text/javascript">
$(function() {
var submit_form = function(e) {
$.getJSON($SCRIPT_ROOT + '/add_numbers', {
nm_height: $('input[name="nm_height"]').val(),
mn_material: $('input[name="mn_material"]').val(),
lc_height: $('input[name="lc_height"]').val(),
li6_enrichment_fraction: $('input[name="li6_enrichment_fraction"]').val()
}, function(data) {
$('#result').text(data.result);
$('input[name=nm_height]').focus().select();
});
return false;
};
$('#calculate').bind('click', submit_form);
//$("#myform").bind('ajax:complete', submit_form);
$('input[type=text]').bind('keydown', function(e) {
if (e.keyCode == 13) {
submit_form(e);
}
});
$('input[name=a]').focus();
});
</script>
<p>
<form name="myform" id="myform" >
<!--<form action="#" method='POST GET'>-->
<p>Height of neutron multiplier pebble bed <input type="number" size="10" name="nm_height" min="10" max="140" step="any" required placeholder='10 to 120'> mm </p>
<p>Neutron multiplier material <input type="text" size="10" name="mn_material" required placeholder='Be or Be12Ti'> Be or Be12Ti</p>
<p>Height of lithium ceramic pebble bed <input type="number" size="10" name="lc_height" min="10" max="140" step="any" required placeholder='1 to 60'> mm </p>
<p>Lithium 6 enrichment <input type="number" size="10" name="li6_enrichment_fraction" min="0" max="100" step="any" required placeholder='60 to 100'> %</p>
<button id="calculate" type="submit">predict TBR</button>
<input id="calculate" type="submit"></input>
</form>
TBR =<span id="result">?</span>
<br>
The TBR is quoted with a 95% <a href='http://www.stat.yale.edu/Courses/1997-98/101/confint.htm'> confidence interval </a>
<!--<p>calculate server side-->
{% endblock %}
I am in the same case and I have decided to use either regular expressions in the validation or just javascript .
regular expression is more powerful and faster but needs time, javascript is easier and slower. so it is a deal.
Let the backend handle the validation using something like WTFForms. Then all you need to worry about is submitting the data to the API endpoint and dealing with the responses that come back.
Avoid doing any validation strictly on the frontend because it's not secure and more likely to be tricked compared to backend validation.
{% extends "layout.html" %}
{% block body %}
<script type="text/javascript">
$('#myform').on('submit', function() {
var res = $.ajax({
url: '/add-numbers'
data: $(this).serialize(),
...
});
res.done(function(data) {
// get successful calculation back...
});
res.fail(function() {
// handle validation or calculation errors...
});
});
</script>
{% endblock %

Using asynchronous POST requests with Python's Bottle?

I'm trying to create a turn-based strategy game (think Dominion) in Python. The core game objects and methods are Python classes with methods in them (just typical OO stuff). The UI is an HTML client using Bottle. I'm aiming for an entirely asynchronous approach. So the sole page's content is generated from the Python objects, and I want submits from the page to update those objects without ever leaving the page by going back through the bottle webserver (using jQuery AJAX for this).
At the moment I'm working on a basic chat system that retrieves player-written messages and stores them as Chat objects (containing player and text data, nothing else). These objects are then written to a chat window using AJAX that updates the windows once every second. The HTML format of the chat lines is <div class="chatLine"><p>Player > </p><p>Text</p></div> Pretty standard stuff.
This basic diagram might make it a little clearer, even though it isn't really technical, more conceptual:
My BottleUI.py (this is what I run to start the server):
from Populate import * # Everything in Populate can now be directly called.
# NOTE: Populate allows access to "bottle" and "Main"
# This ensures the css file is available
#route('/theCSS')
def static():
return static_file('main.css', root='static')
#route('/citadel/:game/:player')
def gamePage(game, player):
if not (game.isdigit()) or not (player.isdigit()):
return "Invalid page."
game = int(game)
player = int(player)
if ((game >= 0) and (game < listOfGames.__len__())) and ((player >= 0) and (player < listOfGames[game].listOfPlayers.__len__())):
return '''
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/theCSS">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<!-- Sample AJAX script below, change as needed -->
<script type="text/javascript">
$(document).ready(function() {
$('#chatForm').submit(function(e) {
$.ajax({
type: 'POST',
url: "/AddToChat/''' + str(game) + '''/''' + str(player) + '''",
success: function() {
$('#chatInput').val("");
}
});
e.preventDefault();
});
});
setInterval("updateChat();", 1000);
$(function() {
updateChat = function() {
$('#chatText').load('/GenerateChat/''' + str(game) + '''');
};
});
</script>
<!-- Script to scroll to bottom of divs - needs to be changed into called function -->
<script type="text/javascript">
window.onload = function () {
var objDiv = document.getElementById("gameFlow");
objDiv.scrollTop = objDiv.scrollHeight;
objDiv = document.getElementById("chatText");
objDiv.scrollTop = objDiv.scrollHeight;
};
</script>
</head>
<body>
<div id="container">
<!-- Make this have background-image with the game number displaying programmatically -->
<div id="banner">
<h1>Citadel - Game ''' + str(game) + ''', Player ''' + str(player) + '''</h1>
</div>
<div id="main">
<div id="leftPanel">
<div id="playerTotals">
<h4>Player Totals:</h4>
<div id="totalsText">
<p>Money:</p>
<p>Population:</p>
<p>Troops:</p>
<p>Friend:</p>
<p>Enemy:</p>
</div>
<!-- Player totals go here (money, population/limit, troops, friend, enemy) -->
<div id="totalsNums">
</div>
<div class="clear"></div>
</div>
<div class="leftSegment">
<h4>Troop Cards:</h4>
<!-- Player's troopCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Territory Cards:</h4>
<!-- Player's territoryCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Region Cards:</h4>
<!-- Player's regionCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Resource Cards:</h4>
<!-- Player's resourceCards here -->
<select size=2>
</select>
</div>
<div class="leftSegment">
<h4>Diplomacy Cards:</h4>
<!-- Player's diplomacyCards here -->
<select size=2>
</select>
</div>
<div id="chatPane">
<form id="chatForm" method="POST" action="/AddToChat/''' + str(game) + '''/''' + str(player) + '''">
<textarea name="theChatText" id="chatInput"></textarea>
<input id="chatSubmit" class="button" type="submit" value="Send" />
</form>
</div>
<div class="clear"></div>
</div>
<div id="rightPanel">
<!-- Game flow goes here (shows current player/phase, attacks with results, etc) -->
<div id="gameFlow">
</div>
<!-- Player turn stuff goes here (changes depending on which phase, etc) -->
<div id="playerActions">
</div>
<!-- Chat goes here (implement last) -->
<div id="chatText">
</div>
<div class="clear"></div>
</div>
</div>
</div>
</body>
</html>
'''
else:
return "Invalid page."
run(host='localhost', port=8080)
And here's my Populate.py (this is where my AJAX #route methods are stored):
"""
This module contains the bottle routs for AJAX population of the various parts
of the game page.
"""
from bottle import route, run, template, static_file, request
from Main import * # Everything in Main can now be directly called.
globalBegin()
#route('/AddToChat/:game/:player', method='POST')
def AddToChat(game, player):
theText = request.POST.get('theChatText', '').strip()
game = int(game)
player = int(player)
listOfGames[game].listOfPlayers[player].addChat(theText)
#route('/GenerateChat/:game')
def GenerateChat(game):
chatText = ""
game = int(game)
for line in listOfGames[game].chatList:
chatText += '<div class="chatLine"><p>'
chatText += line.player.name
chatText += ' > </p><p>'
chatText += line.text
chatText += '</p></div>'
return chatText
The problem is, the 'chatForm' form isn't working as intended. AddToChat() seems to think that request.POST.get('theChatText', '') is a NoneType when I try to submit text.
So yeah, I'm stumped as to why it's doing this. As far as I can see, 'theChatText' should be a valid key in the POST dict.
I'll also just state that all my core game logic works (even though it's pretty clear that isn't the problem here).
Any help is appreciated.
Original jQuery function:
$(document).ready(function() {
$('#chatForm').submit(function(e) {
$.ajax({
type: 'POST',
url: "/AddToChat/''' + str(game) + '''/''' + str(player) + '''",
success: function() {
$('#chatInput').val("");
}
});
e.preventDefault();
});
});
data: $(this).serialize(), needed to be added, like so:
$(document).ready(function() {
$('#chatForm').submit(function(e) {
$.ajax({
type: 'POST',
url: "/AddToChat/''' + str(game) + '''/''' + str(player) + '''",
data: $(this).serialize(),
success: function() {
$('#chatInput').val("");
}
});
e.preventDefault();
});
});
Otherwise the server (or Bottle in this case) won't be able to read the submitted form.

Categories