How to passing value to new template by click on href function? - python

i want to click on href and do javascript function to post some value to python and render new template with that data, this is my code.
index.html
<!DOCTYPE html>
<html lang="en">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<body>
Click
<script>
var jsvariable = 'hello world'
function myFunction(){
$.ajax({
url: "/getvalue",
type: "post", //send it through get method
data:{'jsvalue': jsvariable},
})
}
</script>
</body>
</html>
server.py
from flask import Flask, render_template, request, url_for
app = Flask(__name__)
#app.route('/', methods=['GET','POST'])
def index():
return render_template('index.html')
#app.route('/getvalue', methods=['GET','POST'])
def getvalue():
data = request.form['jsvalue']
print(data)
return render_template('index2.html', data=data)
if __name__ == "__main__":
app.run(debug=True)
Now the data that pass from ajax function to getvalue() in python that work properly but it not render to new template, so how can i fix that.

Mate you don't even need ajax for this(I even think it's what's making a problem for you, correct me if I'm wrong but ajax makes a background POST request, it's like the template gets rendered to the background, you need a foreground request)
Also bad practice, you put your script before the body, it shouldn't even go in the head but as further down as possible
Why is using onClick() in HTML a bad practice?
server.py
#app.route('/getvalue', methods=['GET','POST'])
def getvalue():
if request.method=='POST':
data = request.form['jsvalue']
return render_template("index2.html", data=data)
index.html
<form action = "/getvalue" method = "POST">
<input type = "hidden" name = "jsvalue"/>
<input type = "submit" value = "submit"/>
</form>
<script>
var jsvariable = 'hello world';
var el=document.getElementsByName("jsvalue");
el[0].value=jsvariable;
</script>
index2.html
{{data}}

Related

Submit Flask form without re-rendering the page

I'm building a form with flask, below is the simplified version of my flask server
app = Flask(__name__)
#app.route("/", methods = ["POST", "GET"])
def main_page():
if request.method == "POST":
# some cool stuff
return render_template("main.html")
if __name__ == "__main__":
app.debug = True
app.run()
The problem is that it re-renders the page when the user submits the form, jumping to the top of the page. That makes the user experience kinda bad.
How to get the data of the form without re-rendering the entire page?
If you want to submit the form data, but don't want to completely re-render the page, your only option is to use AJAX.
In the following example, the form data is sent using the Fetch API. The processing on the server remains essentially the same because the form data is submitted in the same format.
However, since there is usually a response in JSON format here, I advise outsourcing the endpoint, so that there is a separation between HTML and JSON routes.
from flask import (
Flask,
jsonify,
render_template,
request
)
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload', methods=['POST'])
def upload():
# Same cool stuff here.
print(request.form.get('data'))
return jsonify(message='success')
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Index</title>
</head>
<body>
<form name="my-form" method="post">
<input type="text" name="data" />
<input type="submit" />
</form>
<script type="text/javascript">
(uri => {
// Register a listener for submit events.
const form = document.querySelector('form[name="my-form"]');
form.addEventListener('submit', evt => {
// Suppress the default behavior of the form.
evt.preventDefault();
// Submit the form data.
fetch(uri, {
method: 'post',
body: new FormData(evt.target)
}).then(resp => resp.json())
.then(data => {
console.log(data);
// Handle response here.
});
// Reset the form.
evt.target.reset();
});
})({{ url_for('upload') | tojson }});
</script>
</body>
</html>

Python Flask: getting URL when moving to production

I'm using Python from quite some time but I am totally newbie in Flask. Can you help me with two simple questions I have been strugling two days?
I have a simple Flask app that supposed to import a XLSX or CSV, parse it and create a zip file to download. While I am begginig work on the parse part, I got an error when uploading the file, and I found out that the app is not saving the file altough it works when running flask locally.
This is the code:
test2.py
``` from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
import pandas as pd
app = Flask(__name__)
def work(arquivo):
df = pd.read_excel(arquivo)
return(str(df.shape))
#app.route('/')
def start():
return "acesse /upload"
#app.route('/upload')
def upload_file():
return render_template('upload.html')
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
if __name__ == '__main__':
app.run(debug = True)
```
And this is the upload.html file that I put on templates folder in production (the app runs on http://www.fabianocastello.com.br/test2/upload):
<html>
<body>
<form action = "http://www.fabianocastello.com.br/test2/uploaded" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
when locally, the upload.html file that works is this:
<html>
<body>
<form action = "http://localhost:5000/uploader" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
The error I got after uploading the file is this:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
My questions are these:
1. Why the production app is not saving the uploaded file in "inbox" folder?
2. Is there a way that I substitute the URL in upload.html file from a variable so to I do not have to manually change the file before upload?
Thank you all in advance.
This is what the url_for method is for. That will automatically fix "http://localhost:5000/uploader" for you.
However, <form action = "http://www.fabianocastello.com.br/test2/uploaded" ...> points at a bigger misunderstanding. It would be horrendous if you had to alter every route in your templates moving from development to production. Your Flask routes needn't point to the specific domain that you're running your app on; they need only point to the endpoint of the server running your app (which might be gunicorn, for example). The Mega Tuorial might be helpful here for deployment. There's also more info in the deployment docs.
With that out of the way, there's other issues that need to be dealt with:
You have two route functions with the same name - upload_file. Why? It doesn't matter that you decorated them with different URLs, you can't have two functions with the same name in the same namespace.
The second upload_file is set to accept both GET and POST requests, but you only handle the POST case. Sending a GET request to this route will error.
This fixes the form:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
</body>
</html>
This consolidates the two route functions into one:
#app.route('/uploader', methods = ['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
f.save('./inbox/'+secure_filename(f.filename))
a = work('./inbox/'+secure_filename(f.filename))
return 'file uploaded successfully '+a
else:
return render_template('upload.html')
return 'file uploaded successfully '+a is going to give a garbage result, if any, though. It's not going to render a template with the message, it's just going to be unstyled text. It looks like you want AJAX, which would look something like this:
<html>
<body>
<form action = "{{ url_for('upload_file') }}" method = "POST"
enctype = "multipart/form-data" id="upload_file_form">
<p>Arquivo</p>
<input type = "file" name = "file" accept=".csv, .xlsx" </input>
<input type = "submit" value="Enviar arquivo"/>
</form>
<div id="response_div"></div>
</body>
<script>
$("#upload_file_form").submit(function(e) {
e.preventDefault();
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(),
context: form,
success: function(resp) {
$("#response_div").html(resp);
}
});
});
</script>
</html>

String POST Request to Flask

I'm trying to implement a simple dashboard with Flask that will:
Accept a user text input, with a "submit" button. POST this user input to flask.
Flask accepts this input, does some stuff to it, then makes a GET request to another API.
This GET request returns data and shows it somehow (can just be console.log for now)
As an example, with the star wars API:
User inputs name of a Star Wars character (assume no spelling errors)
Flask reads this input name, and maps it to an ID number, because the Star Wars API accepts id numbers. Form a GET request to the Star Wars API, to get full character information.
For now, we can just console.log character information (e.g. "height", "mass", etc.)
What I have now:
app.py
from flask import Flask, jsonify, request, render_template
import random
import json
app = Flask(__name__)
#app.route("/")
def index():
return render_template('index.html')
#app.route("/form_example", methods=["GET", "POST"])
def form_example():
if request.method == "POST":
language = request.form("character_name")
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
# starwars_dictionary is a dictionary with character_name:character_number key-value pairs.
# GET URL is of the form https://swapi.co/api/people/<character_number>
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>py-to-JS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h3>Sample Inputs</h3>
<ul>
<li>Luke Skywalker</li>
<li>C-3PO</li>
<li>R2-D2</li>
</ul>
<form method="POST">
Enter Name: <input type="text" name="character_name"><br>
<input type="submit" value="Submit"><br>
</form>
</body>
</html>
In this current form, when I run the app, it returns "Method not allowed; this method is not allowed for the requested URL".
I'm not sure what I'm missing; it's probably just not wired together properly but I'm not sure what the proper syntax is.
Working version after implementing the accepted answer:
app.py
from flask import Flask, jsonify, request, render_template
import requests
import random
import json
app = Flask(__name__)
#app.route("/index", methods=["GET", "POST"])
def index():
#character_height = "" # init a default value of empty string...seems unwieldy
if request.method == "POST":
character_name = request.form.get("character_name")
# Map user input to a numbers
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
char_id = starwars_dictionary[character_name]
url = "https://swapi.co/api/people/"+char_id
response = requests.get(url)
response_dict = json.loads(response.text)
character_height = response_dict["height"]
return render_template("index.html", character_height=character_height)
return render_template("index.html")
##app.route("/form_example", methods=["GET", "POST"])
#def form_example():
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html>
<head>
<title>py-to-JS</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<h3>Sample Inputs</h3>
<ul>
<li>Luke Skywalker</li>
<li>C-3PO</li>
<li>R2-D2</li>
</ul>
<form method="POST" action="/index">
Enter Name: <input type="text" name="character_name"><br>
<input type="submit" value="Submit"><br>
</form>
{{ character_height }}
</body>
</html>
Probably the form is posting to the / endpoint, because you didn't declare a form action.
Needs to be more like:
<form method="POST" action="/form_example">
Or if you want to get snazzy and use Jinja's url_for function:
<form method="POST" action="{{ url_for('form_example') }}">
EDIT: That said, you could handle this with a single route function:
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
language = request.form("character_name")
starwars_dictionary = {"Luke Skywalker":"1", "C-3PO":"2", "R2-D2": "3"}
# Logic to query remote API ges here.
else: # Assume method is GET
return render_template("index.html")
Then make the form action {{ url_for('index') }}

After flask redirect i have invalid data

I want to use Dropzone.js with Flask. After uploading file i want save file and show uploaded file name in another page (after redirect). But in browser i receive file name equals "sample_value". How to fix this?
Python
import os
from flask import Flask, render_template, request,redirect, url_for
app = Flask(__name__)
app.config['UPLOADED_PATH'] = os.getcwd() + '/upload'
#app.route('/')
def index():
# render upload page
return render_template('index.html')
#app.route('/upload', methods=['GET', 'POST'])
def upload():
n='sample_value'
if request.method == 'POST':
for f in request.files.getlist('file'):
n=f.filename
f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
print(f.filename)
# There i get real file name
n=f.filename
return redirect(url_for('found', file_name=n), code=307)
#app.route('/found',methods=['GET', 'POST'])
def found():
#There my file name is "sample_value"
print('File name after redirect ', request.args.get('file_name'))
return request.args.get('file_name')
if __name__ == '__main__':
app.run(host='0.0.0.0', port =5000, debug=True, threaded=True)
Template
<html>
<body>
<script src="{{ url_for('static', filename='js/dropzone.js') }}"></script>
<script src="{{ url_for('static', filename='js/jquery.js') }}"></script>
<form action="{{ url_for('upload') }}" class="dropzone" id="my-dropzone" method="POST" enctype="multipart/form-data">
</form>
<script>
Dropzone.autoDiscover = false;
$(function() {
var myDropzone = new Dropzone("#my-dropzone");
myDropzone.on("queuecomplete", function(file) {
// Called when all files in the queue finish uploading.
window.location = "{{ url_for('upload') }}";
});
})
</script>
</body>
</html>
How i understand redirection executes before processing request?
I assume that you do not specify the method in your Dropzone, then it uses GET method by default. Check the Dropzone documentation and specify the method as POST.
n="sample_value"
if request.method == 'POST': # This is always false.
...
>>> print(n) # n has never been replaced
sample_value

Output data on same page after form submit

So I created a small flask program which would take a file , do some processing and returns a stream of data using yield.
I am using html form for file upload and submit. The form sends file to a python script and returns the output. The issue is that the output is presented onto a different page because of the form action attribute whereas I need the output on the same page. Probably inside a div tag.
index.html
<script>
if (!!window.EventSource) {
var source = new EventSource('/upload');
source.onmessage = function(e) {
console.log(e)
var byte = e.data;
var res = byte.split(/\s/);
console.log(res[0])
$("#morse").text(res[0].slice(1,));
}
}
</script>
<form action="/upload" method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div> // this is where is need my data
Python code
#app.route('/')
def index():
return render_template('index.html')
#app.route("/upload", methods=['GET', 'POST'])
def streambyte():
if request.method == 'POST':
f = request.files['file']
list_of_items = unAssign(f) # some file processing
def events():
for i in list_of_items:
yield "data: %s\n\n" % (i)
time.sleep(1) # an artificial delay
return Response(events(), content_type='text/event-stream')
This streams the data on http://localhost:5000/upload whereas I need it on http://localhost:5000.
I tried using redirect with Response but it failed saying TypeError: 'generator' object is not callable
You may not need JavaScript to do this...
Since you need the result on the 'index.html' page (i.e http://localhost:5000), you need to create two routes for the same index page.
The first route will load the fresh form (method attribute not set), while the second will reload the process form (method attribute is set to POST). Both routes will point to same index page.
Here below is how your code should look like:-
index.html
<!DOCTYPE html>
<html>
<head>
<title>Flask App - Output data on same page after form submit</title>
</head>
<body>
<form method=post enctype=multipart/form-data >
<p><input type="file" name="file" >
<input type="submit" value="Upload" id="search_form_input">
</form>
<div id="morse" class="info">nothing received yet</div>
<h3>{{ result }}</h3>
<h3>{{ file_path }}</h3>
<!-- this is where is need my data -->
</body>
</html>
Python code
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route("/", methods=['GET', 'POST'])
def streambyte():
# your file processing code is here...
f = request.files['file']
your_script_result = 'This variable holds the final data output'
# your file processing code is here...
return render_template('index.html', file_path = f, result = your_script_result)
if __name__ == '__main__':
app.run(debug=True)
Read more from this link: Send data from a textbox into Flask?

Categories

Resources