Hi all: I'm using Flask to develop a simple web app. I have a python function that return a collection of objects that I then want to render inside a template (i.e {{ object1.value }}) in a html page. I was thinking about creating a dictionary containing the object values that would then be passed on to the page as a jsonify string through a GET request.
The Flask app looks like this:
#app.route('/')
def hello():
python_func(object1,object2,object3...)
data = json.dumps({object1.key: object1.value, object2.key: object2.value ...})
if request.is_xhr:
return jsonify(data=data)
if request.method == "GET":
return render_template('main.html',data=data)
if __name__ == '__main__':
app.run()
The html page looks like this:
<div class ="dice">
<div class ="dice-1">
<div class="card" id ="card1" >
<p>{{ data }}</p>
</div>
</div>
And script with an event button. When button is clicked the object values are updated:
<script>
$(function() {
$('#roll-button').click(function() {
$.get('/', function(data){
document.getElementById("card1").innerHTML = data.data;
})
});
});
</script>
This seems to work to pass the object values to the template. I can see a string object with all the object keys and values when I update the page.
My problem is that I don't know how to use the string inside the template. I am confused about the documentation (https://docs.python.org/3.4/library/json.html) on the subject about decoding json.
Hence this question: how can parse the string containing all the object values to render inside the template to ideally look like this: {{ object1.value }}, {{ object2.value }}, {{ object3.other_attribute }} etc...
Can I create a python dictionary from the json string for me to use in the template?
Thank you for your help!
Cam
Don't pass JSON to your template, pass a dictionary. If you want non-dynamic rendering of data in HTML go with Jinja and flask's built in stuff.
from flask import render_template
#api.route('/foo', methods=['GET'])
def foo():
my_dictionary = {'a':'b', 'foo':'baz'}
return render_template('foo.html', my_data=my_dictionary)
Then in your html template
<!DOCTYPE html>
<html lang="en-ca">
<body>
{% for key in my_data %}
<h1>{{key}}</h1>
<h1>{{my_data.key}}</h1>
{% endfor %}
</body>
</html>
If you want to make the data available as a javascript object on the client side for live updating you will have to make an ajax call and do some templating on the front-end. If you don't mind page reloads just stick with flask's templating engine.
Related
I am writing a web application which accepts user input in an HTML form written in Jinja2.
Based on the input, I set variables defined in Jinja2 which I then want to pass as positional arguments to another function defined in either my __init__.py file or main.py file.
Python version==3.6.8
Flask==2.0.1
Jinja2==3.0.1
I attempted many ways to achieve this but could not so (got an undefined error when trying to call the function), including following the suggestions in this thread: Call a python function from jinja2
The file structure of my web app looks like so:
https://prntscr.com/1rlt3cl (I can't post images because I don't have enough reputation points so I uploaded it here).
I am receiving user input in my form.html file which I pass to the data.html file.
<form action="/data" method = "POST">
<p>Various inputs<input type = "number" name = "random" /></p>
<p><input type = "submit" value = "Submit" /></p>
</form>
From the data.html file, I want to accept the values inputted by the user, and call a function which sits in all of my __init__.py files (wasn't sure which one Flask actually looks at):
{% set ns1 = namespace(random=None) %}
{% for key,value in form_data.items() %}
{% if key == "iterations" %}
{%- set ns1.iterations = value -%}
{% endif %}
{% endfor %}
{{ clever_function(random) }}
clever_function is defined in the __init__.py file as so:
from jinja2 import Template
def clever_function():
return "Hello"
template = Template("{{ clever_function() }}")
template.globals['clever_function'] = clever_function
When running my application from webapp/main.py which renders the form / data templates like so:
#app.route('/form')
def form():
return render_template('form.html')
#app.route('/data', methods = ['POST', 'GET'])
def data():
if request.method == 'GET':
return f"The URL /data is accessed directly. Try going to '/form' to submit form"
if request.method == 'POST':
form_data = request.form
return render_template('data.html',form_data = form_data)
I receive the following exception:
jinja2.exceptions.UndefinedError: 'clever_function' is undefined
The code you use is a brief description of the exclusive use within a directly defined template.
To use a custom function within a template used by render_template, you have to add it to the dictionary globals of the jinja environment.
from flask import Flask
from flask import render_template, request
def clever_function(value):
return value**2
app = Flask(__name__)
app.jinja_env.globals.update(clever_function=clever_function)
#app.route('/form')
def form():
return render_template('form.html')
#app.route('/data', methods=['POST'])
def data():
return render_template('data.html', form_data=request.form)
From now on, this function can be called in every template which is loaded and called up with this environment.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p>Result: {{ clever_function(form_data.get('random', 0) | int) }}</p>
</body>
</html>
Please keep in mind that the defined function must accept parameters if you want to call it up with any.
Errors have also crept in when using the namespace object. You should take another look at the documentation for the assignments in jinja2.
Here is a small example based on your code.
{% set ns1 = namespace(random=0) %}
{% for k,v in form_data.items() %}
{% if k == 'random' %}
{% set ns1.random = v | int %}
{% endif %}
{% endfor %}
<p>Value is {{ns1.random}}</p>
I have a flask dropzone to upload files.
Once an upload is done I want to print a log text on the html site
It works so far, the only problem is - the div tag doesn't update the log text after the second upload. The website stays with the text from the first upload.
index.html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script>
$(function(){
window.setInterval(function() {
loadNewLogger()
}, 500)
function loadNewLogger(){
$.ajax({
url:"/write_log",
type: "POST",
datatype: "json",
success: function(data){
$(logger).replaceWith(data)
}
});
}
});
</script>
<body>
<div style="color:rgb(0, 0, 0);text-align:center">
CDR PIPELINE </div>
{{ dropzone.create(action='upload') }}
{{ dropzone.load_js() }}
{{ dropzone.config() }}
<div id="logger">
{{ logger }}
</div>
</body>
</html>
logger.html (otherwise I would render index.html twice)
<div id="logger">
{{ logger }}
</div>
excerpt from flask_app.py:
#app.route('/',methods=['POST','GET'])
def upload():
if request.method == 'POST':
f = request.files.get('file')
f.save(os.path.join(app.config['UPLOADED_PATH'],f.filename))
upload.logger = ""
es.main()
upload.logger = es.main.result
return upload.logger
return render_template('index.html')
#app.route('/write_log',methods=['POST'])
def log():
logger = upload.logger
return jsonify('', render_template('logger.html', logger=logger))
Why is it updating the text from upload.logger only once?
First of all, instead of JQuery AJAX, I would recommend using PHP. For example, when you upload files from index.html you can redirect to upload.php, and you can display the message using "echo"
But even if you aren't comfortable with that, don't get too much into Python or JQuery. Your problem can very easily be solved with plain JavaScript:
Here is your html element in logger.html:
<div id="logger"></div>
In flask app.py:
document.getElementById("logger").innerHTML="Your message to be displayed"
I would also recommend you to remove setinterval in index.html as people don't like dynamically reloading blocks
I am new to Flask. I want to run my Python project, when the start button is pressed from the HTML page and display the string which is returned from the Python code, on the HTML page. I am using Python flask.
This is the HTML file with the button.(The name of the HTML file is json.html)
<!DOCTYPE html>
<html>
<body>
<h1>Smart Job Interviewer</h1>
<button type="button">Start the Interview</button>
</body>
</html>
Following is the Python flask file. newexecutiontest is my python file and run() is the function that I need to run. This function returns a string and I want to display it on the HTML page.
from flask import Flask
from TextToSpeech import newexecutiontest
from flask import render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('json.html')
def dynamic_page():
return newexecutiontest.run()
if __name__ == '__main__':
app.run(host='0.0.0.0', port='8000', debug=True)
When I run the code and load the web page it says that "This site can't be reached"
Can someone please help me to achieve the above task.
Thank you in advance.
Try changing the IP to localhost or 127.0.0.1
You should keep the html template under the folder /templates
You could make the button part of a form, so that it is routed back to your python module on click (just printing a string on button click could more easily be done with javascript, but I assume run() performs some logic as well). Also add some input field to the form so you can know it was submitted:
<form method="GET">
<input type="hidden" name="start">
<button type="submit">Start the Interview</button>
</form>
Now in the flask file, you can perform a basic check to see if "start", or whatever name you gave your input, exists in the get request arguments - which would mean the form was submitted. It is possible to pass arguments to an html file, so we will pass None if the form wasn't submitted or the desired string if it was:
from flask import request
#app.route('/')
def index():
return render_template('json.html', test_str=dynamic_page() if request.args.get("start") is not None else None)
And finally, you can check the value of test_str in the html file and print it accordingly, using the jinja templating engine. Logic is declared between {% and %}, while evaluations are declared between {{ and }}. Adding this to the html file where you want the string to be printed should work:
{% if test_str is not none %}
<p>{{ test_str }}</p>
{% endif %}
I try to learn Flask and I can't find an answer to the question: Is there a way to get a “clean” URL, that can be bookmarked, if I use GET as form method with the Flask-WTF plugin?
If I use in a template the method POST:
<form method="POST" action="">
The URL in the browser will not alter, in Flask's debug mode it would be:
http://127.0.0.1:5000/
If I submit the form.
If I use the method GET, the URL would look like this:
http://127.0.0.1:5000/?name=test&submit=Submit&csrf_token=1453393786%23%23a327427d
But I would like to use the Flask-WTF plugin to make forms and get back a nice, bookmarkable URL in the browser like this:
http://127.0.0.1:5000/?name=test
Is that possible?
What I try to achieve is something like this in PHP:
<?php
if( $_GET["name"] ) {
echo "Hello, ". $_GET['name']. "!";
exit();
}
?>
<html>
<body>
<form action = "<?php $_PHP_SELF ?>" method = "GET">
Name: <input type = "text" name = "name" />
<input type = "submit" />
</form>
</body>
</html>
After submitting, I get this URL:
http://127.0.0.1/test.php?name=test
I can copy this link, send it to someone else, he or she can open it with a browser and will get the same result. It is simply done, by using the method GET in the form. Look at this:
http://www.utrace.de/?query=8.8.8.8
With Flask I cloud do this:
http://127.0.0.1/query/8.8.8.8
But what, if I would use more than one parameter? In PHP it would look like this:
http://127.0.0.1/?para1=8.8.8.8¶2=US
I've tried it, using this code (lent from Miguel Grinberg):
Program:
from flask import Flask, render_template
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required, Length
app = Flask(__name__)
app.config['SECRET_KEY'] = 'top secret!'
class NameForm(Form):
name = StringField('What is your name?', validators=[Required(),
Length(1, 16)])
submit = SubmitField('Submit')
#app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)
if __name__ == '__main__':
app.run(debug=True)
Template:
<form method="GET" action="">
{{ form.name.label }} {{ form.name(size=16) }}
{% for error in form.name.errors %}
{{ error }}
{% endfor %}
<br>
{{ form.submit() }}
{{ form.hidden_tag() }}
</form>
{% if name %}
<h1>Hello, {{ name }}!</h1>
{% endif %}
I would suggest factoring this out into its own view. You should use CSRF for forms, and you should try to separate GET and POST interfaces.
Forcing one piece of code or function to do multiple things sometimes might seem cleaner, but what happens is you add to maintainability costs and make things less clear down the road.
How you'd do this in Flask:
#app.route('/<name>/', methods=['GET'])
Now, this does not give you the validation. You can either do this yourself, or use a library such as Marshmallow: https://marshmallow.readthedocs.org/en/latest/
In your example Marshmallow would be overkill, but it is great if you plan on expanding your API. It will allow you to take more complicated JSON blobs and validate and serialize them into Python objects.
Also, I'd look into why you're using GET over POST. POST should be used to create new data on a server, GET should be used to get that information.
EDIT (After your edits):
With Flask I cloud do this:
http://127.0.0.1/query/8.8.8.8
But what, if I would use more than one
parameter? In PHP it would look like this:
You would make your Flask view like this:
#app.route('/<name>/<another_parameter>/<another>/', methods=['GET'])
def some_route_name(name, another_parameter, another):
However, if you want to accomplish what you want with ONE form, you would have to turn CSRF off. Your PHP example is not using CSRF.
I suggest this:
1) Creating a new view, as I originally suggested
2) On your view with the form, have it POST to itself, then redirect to the new view, like the below:
if form.validate_on_submit():
return redirect(url_for('name_of_new_view', name=form.name.data))
3) In this new view, put your form, but have that form POST to your OLD view. Make sure you're including the CSRF token when you POST! Like here:
{{ form.csrf_token }}
So I'm working on a project using pythong flask, and sqlalchemy.
We have a datatablefull of contracts, that have the primary id of contract_id. I figured we have 2 different options of displaying the contracts.
option 1.
We have a generic view_contract.html template that makes a post request to an app route on flask and returns json of the contract to display on the page. However, I don't like this idea as much because then you can't copy and paste certain contract links to other people, since it will be the same generic webpage url
option 2.
We have an app route in flask that's #app.route(contract/< contract_id >/view) and we query the database to show that specific contract_id. However, I only know how to display the data in python using flask.jsonify. How can I generate html in the url as well?
tldr:
How do I generate a webpage such as contract/112431/view that loads the contract of id 112431 in an html form. As of right now I can only do it using python and that doesn't allow me to generate any of the html forms or details that I'd like
here's what I have right now... How would I add html to this page so it works for each contract
#app.route('/contract/<contract_id>/profile')
def view_suitor_profile(contract_id):
cs = Contract.query.filter(Contract.contract_id == contract_id).all()
cs_list = []
for c in cs:
con = {
"con id": c.contract_id,
"lcat" : c.lcat,
"desired skills" : c.desired_skill,
"mandatory skills" : c.mandatory_skill,
"location" : c.work_location
}
c_list.append(con)
return flask.jsonify(c_list=c_list)
Option 1
Use AJAX. Its easy to implement.
Option 2
simple_contracts.py
#app.route('/simple/<contract_id>/page', methods=['GET', 'POST'])
def contracts(contract_id):
result = dbs.Contracts.select().where(dbs.Contracts.id == contract_id).first()
return render_template('contracts.html', result=result)
contracts.html
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>simple contracts</title>
</head>
<body>
{% if result %}
{% for contract in result %}
{{ contract.id }} <br/>
{{ contract.name }} <br/>
{{ contract.price }} <br>
{# annotation: `contract` type object, have access in methods/attributes ex namedtuple in jinja templates
ex: in python: somelist = {'id': 12, 'name': 'contractIBM', 'price':1223 }
access: somelist['id']
in jinja: somelist.id => 12
somelist.name => contractIBM etc...
#}
{% endfor %}
{% endif %}
{#for send DATA in route use ahref args link or forms#}
Get info contract 123
</body>
</html>
This gives an example of option 2.
In your view creating the list of data, instead of returning it as a json you should render a template with the data.
return flask.render_template("template.html",
c_list=c_list)
In your html you can loop through the list and extract all the elements
<div>
{% for entry in c_list %}
<p> Contract: </p>
<p>{{ entry }}</p>
{% endfor %}
</div>
Then you can try and extract more detail from the {{ entry }} element by trying something like {{ entry.lcat }}
This allows you to create a html template which will be populated with the data that you pass along it, when calling render_template. So as long as you want the same layout, but with different data, this is one approach