Python/Flask/HTML - Render HTML in new window instead of home page - javascript

I have a Python code where I'm using Flask to create a webpage. In the home page, I'm filling out a form, submitting using a button and it displays a table based on the inputs.
The problem i'm having is that once I click the button to submit the form, it renders the table on that same webpage. I would like to create a new window using JavaScript window.open() or whatever other method you might suggest to render that table inside the new window and leave the home page as it is. I tried looking around and I can't seem to get anything to work. I've read through this question and this question. But those suggestions don't seem to match what i'm looking for.
This is my code:
Python code
from flask import Flask, render_template, request,
app = Flask(__name__)
def get_table(user_input):
...
return dict //returns list of dictionaries, for example...
//dict = [{'name':'Joe','age':'25'},
// {'name':'Mike','age':'20'},
// {'name':'Chris','age':'29'}]
#app.route("/")
def home():
return render_template('home.html')
#app.route("/table", methods = ['POST'])
def table():
user_input = request.form['input']
dict_table = get_table(user_input) //return list of dictionaries
return render_template('table.html', dict_table=dict_table)
if __name__ == '__main__':
app.run(debug=True)
home.html
<!DOCTYPE html>
<html>
<head>
<title>Homepage</title>
</head>
<body>
<form action="/table" method="post">
<select name="input">
<option value="1">Input</option>
</select>
<button type="submit">Click Me!</button>
</form>
</body>
</html>
table.html
<!DOCTYPE html>
<html>
<head>
<title>Table</title>
</head>
<body>
<table id="table">
{% if dict_table %}
<tr>
{% for key in dict_table[0] %}
<th>{{ key }}</th>
{% endfor %}
</tr>
{% endif %}
{% for dict in dict_table %}
<tr>
{% for value in dict.values() %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</body>
</html>
Could someone explain in a clear way how I can click the form submit button on my homepage, stay on the homepage home.html, and make the table from table.html open up in a new window (maybe using window.open() from JavaScript or something else)?
I would appreciate it if someone could walk me through the steps on how to do this with my code provided and show me specifically where to call functions and things like that. I'm new to Flask/HTML/JS and I'm just trying to learn for personal use and I'm getting frustrated reading links and documents that show just how to display a URL like google.com in a new tab, which is not what I want. Thanks!

Step1: Check out this link that explains how to use Jquery and Ajax with FLASK
The key concept here is AJAX (asynchronous JavaScript and XML). In short, it is an architecture that makes it possible to send requests to the server in the background (called Asynchronous requests) and then modifies the content of the page currently displayed by the web browser according to the result received from the server, avoiding as well as the server does not transmit the complete page again.
Step2: A solution to your problem
First we write the routes:
from flask import Flask, render_template, request,
app = Flask(__name__)
user_input = None
def get_table(user_input):
...
return dict # returns list of dictionaries, for example...
# dict = [{'name':'Joe','age':'25'},
# {'name':'Mike','age':'20'},
# {'name':'Chris','age':'29'}]
#app.route('/')
def home():
return render_template('home.html')
#app.route('/_ajax_user_input')
def ajax_user_input():
global user_input
user_input = request.args.get('user_input', 0, type=int)
return "ok"
#app.route("/table")
def table():
x = user_input
dict_table = get_table(x) # return list of dictionaries
return render_template('table.html', dict_table=dict_table)
After we attack the templates:
home.html:
<select id="input" name="input">
<option value="1">Input</option>
</select>
<button type="button" class="test"> Click Me! </button>
<script>
$(document).ready(function(){
$('.test').bind('click', function() {
$.getJSON($SCRIPT_ROOT + '/_ajax_user_input',{
user_input: $('#input').val(),
},function() {
window.open('http://127.0.0.1:5000/table', '_blank');
});
return false;
});
});
</script>
table.html:
<table id="table">
{% if dict_table %}
<tr>
{% for key in dict_table[0] %}
<th>{{ key }}</th>
{% endfor %}
</tr>
{% endif %}
{% for dict in dict_table %}
<tr>
{% for value in dict.values() %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
Basically here is what happens:
when I click on my button, I call the Javascript script:
$('.test').bind('click', function() {
This sends an ajax request to FLASK, which consists in executing the ajax_user_input() function:
$.getJSON($SCRIPT_ROOT + '/_ajax_user_input',
To this function I send a data (the value selected by the user in the select tag) and this data is stored in the variable user_input:
user_input: $('#input').val(),
On the side of Flask I get the data and I store it in a global variable that I named user_input too:
global user_input
user_input = request.args.get('user_input', 0, type=int)
Then in my script I call a javascript method that allows me to open a url in a new tab (more details here):
window.open('http://127.0.0.1:5000/table', '_blank');
The 'table' route, stores in the variable x the data previously stored in my global variable (user_input), then it calls the function get_table() (by passing him the variable x in parameter) which returns a list of the dictionaries, and finally it returns the page table.html with the list of the dictionaries in parameter:
x = user_input
dict_table = get_table(x)
return render_template('table.html', dict_table=dict_table)
I hope this will help you, even though I am convinced that there are many other ways to do it, perhaps more effective.

Related

How can I submit multiple forms using Django and JavaScript?

I'm working on an app that displays a form on the index page, into which you can enter information to eventually calculate a gross total and output other details based on the form inputs.
My form inherits from form.Forms.
A button, "Add row", creates as many copies of this row as needed.
Each form contains a form with the ID "calc-form". My problem is that only one form is included in the POST method. I would like to process all forms with one click.
How can I include all additional forms in the POST method when I click "Calculate"?
index.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% load static %}
<script type="text/javascript" src="{% static 'app.js' %}"></script>
<table id="table-id">
<tr id='table-row'>
<form id="calc-form" action="{% url 'result' %}" method="post">
{% csrf_token %}
{% for field in form %}
<td>{{ field|as_crispy_field }}</td>
{% endfor %}
</form>
</tr>
</table>
<br>
<input type="submit" value="Calculate" form="calc-form">
<button type="button" id="add-row-btn" onclick="addRow()">Add row</button>
<button type="button" id="delete-row-btn" onclick="deleteRow()">Delete last row</button>
{% endblock content %}
views.py
from django.shortcuts import render
from .forms import TariffCalcForm
def index(request):
form = TariffCalcForm
context = {'form': form}
return render(request, 'clickapp/index.html', context)
def result(request):
context = {}
if request.method == 'POST':
for r in request:
form = TariffCalcForm(request.POST)
if form.is_valid():
context['form'] = form
return render(request, 'result.html', context)
else:
return render(request, 'index.html', context)
app.js
function addRow() {
const formTable = document.getElementById('table-id');
const formRow = document.getElementById('table-row');
const formRowCopy = formRow.cloneNode(true);
formTable.appendChild(formRowCopy);
}
The form wizard may be appropriate. It allows you to progress through a sequence of forms, storing the results of submitting each in the client's session. This is of course in the database, but it can be made transient, and can be an anonymous session. The final submit can process the data from all the forms and delete it from the session (which may be automatic, not sure) before rendering a results page for the user.
There is limited capacity for skipping some forms based on the data in the previous ones.
The other approach is to go client-based. You use one huge form, but with Javascript of some sort to steer the user through it section by section. "Submit" on earlier parts does not involve the server, but just hides the sub-form and reveals the next (or switches between tabs, which the user may also do for himself). Methods can range from simple Jquery up to enormous Javascript frameworks.

flask javascript can't print/log jinja2 request.form variable

Hi all I'm trying to get the value from flask and print it in javascript.
When I do print with a javascript string variable in my form it prints none.
var javascript_variable = "key_name";
// output is: request
console.log('request'+"{{request.form['"+javascript_variable+"']}}");
Also tried
// output is: request
console.log('request'+"{{request.form["+javascript_variable+"]}}");
however when I manually type the key name it works.
// output is: request key_information
console.log('request'+"{{request.form['key_name']}}");
Can someone please tell me what I'm doing wrong.
EDIT: I do not want to hardcode the keyname because the key_name is a changing dynamically
I'm trying to access that variable, so I can populate my select option/checkbox/radiobox option something like <option {% if request.form['key_name'] == "value" %} selected {% endif %} same for checkbox and radio box
In order to do that, you can use the flash statements inside the views in flask as follows,
#blueprint.route('/something.html', methods=['GET', 'POST'])
#login_required
def function():
try:
flash("Submission successful!")
except Exception as e:
traceback.print_exc()
return render_template('something.html')
In jinja, you can access the flash variable in the following way,
{% if get_flashed_messages() %}
{% for message in get_flashed_messages() %}
<script>window.alert("{{ message }}")</script>
{% endfor %}
{% endif %}

How to pass a javascript variable to WTForm? Python/Flask

I am creating a data entry/timing web app using Python and Flask. When the user goes to the log page, they enter in the "session ID" for their task, and the time of form submission and user ID are automatically input to the database as well.
Now I am trying to use Javascript to record when the page is loaded for a task so I have a start time and an end time. I first attempted to POST the javascript variable via AJAX to the back end, but I couldn't seem to get it to work. Now I am trying to have the javascript variable be sent alongside the session ID as part of a "HiddenField". I am not yet at the point of configuring the page load time script, I am just trying to get the Javascript to talk to WTForms to talk to my database commit. Here is my code currently:
views.py
#app.route('/logpage', methods=['GET', 'POST'])
#login_required
def logpage():
form = LogForm()
if form.validate_on_submit():
timer = request.get_data("timer")
entry = LogData(sessionid=form.sessionid.data, user_id=current_user.get_id(), starttime=form.testvar.data, endtime=datetime.utcnow())
db.session.add(entry)
db.session.commit()
return redirect(url_for('home'))
return render_template('logpage.html', form=form)
models.py
class LogForm(FlaskForm):
sessionid = StringField('sessionid', validators=[InputRequired(), Length(min=16, max=16)])
testvar = HiddenField('testvar')
logpage.html
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Active Log Page{% endblock %}
{% block head %}
<!-- Custom styles for this template -->
<link href="static/css/starter-template.css" rel="stylesheet">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="{{ url_for('static', filename='jquery.js') }}">\x3C/script>')</script>
<script src="static/js/timer.js?version=25"></script>
{% endblock %}
{% block body %}
<div class="container">
<div class="form-group">
<form class="form-logpage", method="POST" action="/logpage", id="jsinput">
{{ form.hidden_tag() }}
{{ wtf.form_field(form.sessionid) }}
<div id="jsinput">
{{ wtf.form_field(form.testvar) }}
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Finish Job</button>
</form>
</div>
</div>
{% endblock %}
timer.js
var myData = "dataVariable";
document.getElementById("jsinput").value = "myData";
As you can see, I tried to use document.getElementById to put the variable in logpage.html, but when I hit submit, the starttime field is empty. I've looked around the documentation for a way to put a variable in the WTForm, but I can't find anything relevant.
Before, during some other issues it's showed as null and /x and ''b but I've done a good bit of tinkering since then and the database entry is just ''.
If there is a more elegant way to record page load datetime and record simultaneously to the database with the rest of the data, I would love to hear suggestions. Previous SO post here: Python/Flask: How to tell how long a user spends on a page? (Data entry/time log app)
Here's the solution I came up with for this problem:
Add a hidden field to the form with an id.
Grab the ID in your script and assign a value to it.
In your form.py:
from flask_wtf import FlaskForm
from wtforms import SubmitField
from wtforms import HiddenField
class MyForm(FlaskForm):
my_data = HiddenField(
'my_data',
render_kw={'id':'my_data'}
)
save = SubmitField(
'Save',
render_kw={'id':'save'}
)
create and pass your form in your view and then render the save and my_data fields in your html template using {{ form.save }} and {{ form.my_data }}
Add the following to your javascript:
var saveButton = document.getElementById('save');
saveButton.addEventListener('click', function() {
document.getElementById('my_data').value = 'MY DATA IS HERE!!';
});
you can now access your data in your python view using:
from flask import request
...some code...
request.form['my_data']
It's a late answer, but hopefully useful to someone out there...

Django make the passed data invisable from view source

I pass some data and process in my django template file. It works just fine. However, when i right click and then select "view page source" on my internet browser, i can see all the values that i passed from my view.py. How to hide the values in the template file.
Child.page
{% extends "base.html" %}
{% block title %}My amazing blog{% endblock %}
{% block extra_js %}
<script>
var secret_data = new Array();
function mybutton(){
{% for data in Mysecret%}
// Here, I wanna make the value of data invisable
secret_data.push({{ data.0 }})
{% endfor %}
}
</script>
{% endblock %}
{% block content %}
<input type="submit" name="submitButton" value="Submit" onclick ="mybutton();"> </input>
{% endblock %}
When i right click and select "view to source" on my internet browser, i can see all the values something like that:
<script>
var secret_data = new Array();
function mybutton(){
secret_data.push("Secret-1")
secret_data.push("Secret-2")
}
</script>
I have tried this:
secret_data.push({% csrf_token %}{{ data.0 }})
The values cannot be seen in case of viewing source code of the page, but at the same time it messes up the data that i pass (cannot access data cause the data turns out a div). How can i make my secret_data list invisible so that if someone tries to view source of my page, she would not be able to see the passed values ("Secret-1" and "Secret-2").

How to send values via ajax from django template to views?

I have a select box that calls a function on changes. The function finds the selected value from "Products" selectbox.
I want to throw that selected value to my views.py which after some operations return the list of data and Populates the Destination's selectbox.
I want to use ajax for this purpose. Please help.
My code looks like this:
<script type="text/javascript">
function select_value()
{
var e = document.getElementById("Product");
var prod = e.options[e.selectedIndex].text
console.log(prod)
}
</script>
This is what my selectbox look like:
<table>
<tr>
<td>
<select id="Product" onChange="select_value();">
{% for products in product_name_list %}
<option>{{products|safe}}</option>
{% endfor %}
</select>
</td>
<td>
<select id="dest">
{% for des in destinations_name_list %}
<option>{{des|safe}}</option>
{% endfor %}
</select>
</td>
</tr>
</table>
This is my views.py:
def selection_filter(request,prod):
destination_objs = Destination.objects.filter(product=prod)
destination_name = destination_objs.values_list('name')
destination_name_list = []
for iter_name in destination_name:
destination_name_list.append(iter_name[0].encode('utf-8'))
return render_to_response('temp/test.html',
{"destination_name_list" : destination_name_list},
)
I think the point you might be misunderstanding is that your Django template for your whole page will not be re-rendered "magically". In Django's standard model-view-template paradigm, the template is rendered just once, upon the initial request. The way you've written it, unless there's a default product selection, you're going to have an awkward empty <select> in the first render. But ignoring that...
For a problem like this, you need two views: one for rendering the whole page, and one for providing the Ajax response. There are two main options for the second view: 1) return JSON for interpretation/rendering by your script post-response or 2) return a fully rendered HTML fragment for direct insertion into your DOM. In this case, I'd just go for option 2.
I recommend looking into Jquery, as it makes Ajax and DOM manipulation super simple.
If you've got Jquery, it's as simple as adding to your script:
$.get('/destinations/' + prod)
.done(function (html) {
$(#dest).html(html);
});
(You can do the same thing without Jquery too, but it requires a bit more wrangling)
Your test.html file should contain:
{% for des in destinations_name_list %}
<option>{{des|safe}}</option>
{% endfor %}

Categories