This is my first attempt at deploying a machine learning application and also my first time using flask. Essentially the user will fill out a form on an html page and the input from the form will be used as input to the machine learning model which is saved in a pickle file, model.pkl in the code below. I am running into one snag that I can't seem to break past..
Every time I submit from index.html and post to result.html I'm receiving a 404 error.
script.py:
#importing libraries
import os
import numpy as np
import flask
import pickle
from flask import Flask, render_template, request
app=Flask(__name__)
#app.route('/')
#app.route('/index')
def index():
return flask.render_template('index.html')
def ValuePredictor(to_predict_list):
to_predict = np.array(to_predict_list).reshape(1,12)
loaded_model = pickle.load(open("model.pkl","rb"))
result = loaded_model.predict(to_predict)
return result[0]
#app.route('/result',methods = ['POST'])
def result():
if request.method == 'POST':
to_predict_list = request.form.to_dict()
to_predict_list=list(to_predict_list.values())
to_predict_list = list(map(int, to_predict_list))
result = ValuePredictor(to_predict_list)
if int(result)==1:
prediction='Income more than 50K'
else:
prediction='Income less that 50K'
return render_template("result.html",prediction=prediction)
index.html:
<html>
<body>
<h3>Income Prediction Form</h3>
<div>
<form action="/result.html" method="POST">
<label for="age">Age</label>
<input type="text" id="age" name="age">
<br>
<label for="edu">Education</label>
<select id="edu" name="edu">
<option value="0">High School</option>
<option value="1">College Degree</option>
</select>
<br>
<label for="martial_stat">Marital Status</label>
<select id="martial_stat" name="martial_stat">
<option value="0">not married</option>
<option value="1">married</option>
</select>
<br>
<label for="gender">Gender</label>
<select id="gender" name="gender">
<option value="0">Female</option>
<option value="1">Male</option>
</select>
<br>
<input type="submit" value="Submit">
</form>
</div>
</body>
</html>
result.html:
<html>
<body>
<h1> {{ prediction }}</h1>
</body>
</html>
I can't seem to figure this out. My code never seems to reach the first line in the result() function. As soon as I submit from index.html http://127.0.0.1:5000/result.html throws a 404 error. Any suggestions?
The error is very simple here, the action property in index.html should just be
<form action="/result" method="POST">
instead of
<form action="/result.html" method="POST">
You want to use /result to go through your flask function. Hope this helps!
You are posting to the endpoint /result.html:
<form action="/result.html" method="POST">
... but your route in flask is defined as /result (with no .html):
#app.route('/result',methods = ['POST'])
def result():
...
These two need to match, so consider changing the route to result.html.
Instead of <form action="/result.html" method="POST">
Can Use <form action="{{ url_for('result') }}" method="POST">
Related
I am new to flask and i am using google colab to do some basic flask stuff.
I have a html-form:
<!DOCTYPE html>
<html>
<body>
<h1>The input accept attribute</h1>
<form action="#" method="post">
<label for="img">Select image:</label>
<input type="file" id="img" name="img" enctype="multipart/form-data" accept="image/*">
<input type="submit" value="submit">
</form>
</body>
</html>
Then i have FLASK code as to get the uploaded image:
#app.route("/test", methods=['POST','GET'])
def test_page():
if request.method == "POST":
image = request.files.get('img', '')
image = request.form["img"]
print("img ",image)
return "<h1> yo! </h1>"
else:
return render_template('index.html')
DEBUGGING RESULTS:
#debug
print('request.method', request.method)
print('request.args', request.args)
print('request.form', request.form)
print('request.files', request.files)
request.method POST
request.args ImmutableMultiDict([])
request.form ImmutableMultiDict([('img', 'cat.jpg')])
request.files ImmutableMultiDict([])
The problem is i want to doo some processing on the uploaded image but request.files return nothing and request.form just gives me the name. Please any help will be appreciated.
The enctype attribute should be defined within the form tag, not within the input element. Here, the formatting of the data is defined when the form is transmitted.
This could fix the problem.
<form method="post" enctype="multipart/form-data">
<label for="img">Select image:</label>
<input type="file" id="img" name="img" accept="image/*">
<input type="submit" value="submit">
</form>
I have a simple page with a data entry field and a click button, this will run the API to retrieve the coin data
running the code in a python terminal return with success, but when I try to add it to flask and use the webpage, I get the error 405 method not allowed for the POST.
This is the main python/flask file:
crypto.py
# template libraries
from flask import render_template,url_for,flash,request,redirect,Blueprint
# Coingecko API library
from pycoingecko import CoinGeckoAPI
crypto_simulator = Blueprint('crypto_simulator',__name__)
#crypto_simulator.route('/crypto_simulator', methods=['GET','POST'])
#login_required
def crypto_insert():
if request.form.get("ident") == "formCrypto":
print('Hello')
cg = CoinGeckoAPI()
#crypto_token = request.form.get('crypto_name_html', '')
crypto_token = 'bitcoin'
crypto_currency = 'usd'
response = cg.get_price(ids=crypto_token,
vs_currencies='usd',
include_market_cap='true',
include_24hr_vol='true',
include_24hr_change='true',
include_last_updated_at='true')
crypto_result = response.get(crypto_token,'')
print(crypto_result[crypto_currency])
return render_template('crypto_simulator.html',
formCryptoSimulation=form,
crypto_token=crypto_token,
crypto_currency=crypto_currency,
crypto_result=crypto_result
)
This is the Blueprint routing file:
core.py
# crypto section
#core.route('/crypto_simulator')
def crypto_simulator():
return render_template('crypto_simulator.html')
This is the Flask/Bootstrap front-end:
crypto_simulator.html
{% extends "base.html" %}
{% block content %}
<!-- Simulation Code Start -->
<div class="forms">
<div class="formCrypto">
<form method="post" action="{{ url_for('core.crypto_simulator') }}">
<div class="container">
<div class="row g-3">
<div class="col-sm-3">
<label class="form-label"><b>Crypto Name:</b></label>
<input type="text" class="form-control" name="crypto_name_html" placeholder="Enter Crypto Name" required>
</div>
</div>
</div>
<br>
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
<button id="btn" type="submit" class="btn btn-info">Check Token!</button>
</div>
<input type=hidden name="ident" value="formCrypto">
</form>
<br>
<p>Token: <b>{{crypto_token}}</b>
<p>Price: <b>{{crypto_result}}</b>
</div>
</div>
{% endblock %}
I checked for misspelled lines and anything related but still stuck into how to fix it...
Your form has this:
<form method="post" action="{{ url_for('core.crypto_simulator') }}">
So you are calling the function crypto_simulator in blueprint core:
#core.route('/crypto_simulator')
def crypto_simulator():
return render_template('crypto_simulator.html')
Note that your form does a POST request, so you very logically have to enable the POST method on the function being called like this:
#core.route('/crypto_simulator', methods=['GET','POST'])
I have array of two elements in flask:
from flask import Flask, render_template
import psycopg2
import numpy as np
app = Flask(__name__)
d = [10,12,16,17]
#app.route('/about')
def about():
return render_template('about.html', d=d, length=len(d))
if __name__ == '__main__':
app.run()
And also I have Vertical Scroll Menu in html:
<h1>Vertical Scroll Menu</h1>
<div class="vertical-menu">
change number
{% for i in range(length) %}
select number{{d[i]}}
{% endfor %}
</div>
<input type="text" name="name" required="required">
I need to insert number in array into output form after href clicking.How should I solve my problem?
Demo HTML
<form action="{{url_for('process_number')}}" method="POST">
<select name="number">
{% for i in d %}
<option value="{{d[i]}}">{{d[i]}}</option>
{% endfor %}
</select>
<input type="submit"/>
</form>
Flask function
#app.route('/process-number', methods=['GET', 'POST'])
def process_number():
if request.method == 'POST':
selected_number = request.form['number']
# do what you want
I am very much struggling with a concept and hope someone can offer a code implementation suggestion. I have the following route embedded within my flask app. After working on this for a few days I think I now see the nature of the issue. I have modified my code based on suggestion below in comments.
In the web app now, the names of the data frame columns populate a drop down menu and the user can choose one of those variables and click "Show". That variable name then prints to the screen just so I know my POST from the form is communicating with my function in the flask app.
What I would like to do is create a temp version of the file uploaded so that it exists as long as the web session is open (or is overwritten if a new file upload occurs). Then, the user chooses a variable from the drop down menu and the mean of that variable is computed (that part is commented out in the code below for now but you can see what I have tried).
If the user reads in a new file, then the same process would occur on that new file.
Thanks for any suggestions or supports.
from flask import Flask, render_template, request
import numpy as np
from scipy.stats import binom
from scipy.optimize import minimize
from scipy.stats import norm
from scipy import optimize
from pyodbc import connect
import pandas as pd
import os
import tempfile
app = Flask(__name__)
#app.route('/dataTools', methods=['POST', 'GET'])
def data_tools_upload():
tempfile_path = tempfile.NamedTemporaryFile().name
if request.files:
df = pd.read_csv(request.files.get('file'))
#tempfile_path = tempfile.NamedTemporaryFile().name
df.to_csv(tempfile_path)
if os.path.exists(tempfile_path):
orig_df = pd.read_csv(tempfile_path)
vars = list(orig_df.columns)
var2use = request.form.get("var2use")
#indx = vars.index(var2use)
#df[vars[indx]].mean()
mean = orig_df[vars[4]].mean()
dims = orig_df.shape
message = 'You have data! There are %s rows and %s columns and the variable %s has mean %s' % (dims[0],dims[1],vars[4],round(mean,3))
table = orig_df.head(10).to_html(classes='data', header = "true")
return render_template('upload.html', tables = [table], message = message, vars = vars, var_name = var2use)
var2use = request.form.get("var2use")
return render_template('upload.html', var_name = var2use, message = "What happened?")
if __name__ == '__main__':
app.run(debug=True)
And then the following HTML bit (upload.html):
{% extends "layout.html" %}
{% block body %}
<h3> Read in a data file </h3>
<br>
<form method=post enctype=multipart/form-data>
<input type=file name=file class = "btn btn-outline-secondary">
<input type=submit value=Upload class = "btn btn-outline-secondary">
</form>
<br>
<form class="form-inline" action = "{{url_for('data_tools_upload')}}" method = "POST">
<select type = "text" name="var2use" class="custom-select mr-sm-1">
{% for var in vars %}
<option value= "{{ var }}" SELECTED>{{ var }}</option>"
{% endfor %}
</select>
<button class = "btn btn-primary"> Show </button>
</form>
<center>
<h1>
{{message}}
{{name}}
</h1>
<br>
<small>
{% for table in tables %}
{{ table|safe }}
{% endfor %}
{% endblock %}
</small>
</center>
you need to save file to work with it (the code works perfectly in my app):
f = request.files['file']
tmp_filename = '/tmp/my_tmp_file.csv'
f.save(tmp_filename)
pd.read_csv(tmp_filename)
Here is the detailed documentation on Flask file uploads:
https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/
Full example for your case is below. I think you might have problems with the following three items:
Uploading the file because of form enctype (should be "multipart/form-data")
Uploading because of file size (try my sample)
Processing the form with empty file overwriting yours (see my example for "csv_file_uploaded")
Python (csv_test.py):
from flask import Flask, render_template, request
import pandas as pd
import os
app = Flask(__name__, static_url_path='')
#app.route('/', methods=['GET', 'POST'])
def index():
data = {}
tmp_filename = '/tmp/csv_test.csv'
if request.files:
csv_file_uploaded = request.files.get('file')
if csv_file_uploaded:
f = request.files['file']
f.save(tmp_filename)
if os.path.exists(tmp_filename):
df = pd.read_csv(tmp_filename)
data['columns'] = ', '.join(df.columns)
col_selected = request.form.get('col_selected')
print(col_selected)
data['col_selected'] = col_selected
if col_selected:
mean = df.get(col_selected).mean()
data['mean'] = mean
return render_template('csv_test.tpl', data=data)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
Flask template (csv_test.tpl):
<html><head><title>CSV TEST</title></head>
<body><h1>CSV TEST</h1>
{% if data %}
<div>Columns of the uploaded file: <strong>{{ data.columns }}</strong></div>
<div>Selected column: <strong>{{ data.col_selected }}</strong></div>
<div>Mean of the selected = <strong>{{ data.mean }}</strong></div>
{% endif %}
<fieldset>
<form enctype="multipart/form-data" action="" method="post">
<h2>Provide some CSV with first column in numbers and/or column name</h2>
<div>File{% if data.columns %} (UPLOADED){% endif %}: <input id="file" type="file" name="file"/></div>
<div>Column: <input id="col_selected" type="text" name="col_selected" value="{{ data.col_selected }}"/></div>
<div><input id="submit" type="submit" value="SUBMIT"/></div>
</form>
</fieldset>
</body></html>
CSV file (123.csv) - use it to test the form:
col1,col2
1,2
2,3
3,4
5,6
Using the following:
from flask import Flask, render_template
import beautiful_soup_tidal
app = Flask(__name__)
#app.route('/')
def form():
return render_template('form_submit.html')
#app.route('/richmond', methods=['POST'])
def richmond():
someTides = beautiful_soup_tidal.getTides()
return render_template('richmond.html',someTides=someTides)
if __name__ == "__main__":
app.run(debug=True)
And attempting to render the following (richmond.html):
<div id="content" class="form-group">
<form method="post" action="/richmond">
<label style="vertical-align: middle;">channel depth at mean low water
<input type="number" step="0.1" value = "34.5" name="channelDepth"/>FEET</label><br><br>
<label style="vertical-align: middle;">required underkeel clearance
<input type="number" step="0.1" value = "2" name="underkeelClearance"/>FEET</label><br><br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
I get the following error: 'The method is not allowed for the requested URL.'
If I delete ', methods=['POST']' in the first section the template renders.
The question: How do I render the template successfully using the post method?
i believe this line should also include GET so that you can render the html form first time round before you actually click submit to post it.
#app.route('/richmond', methods=['POST'])
so it would change to
#app.route('/richmond', methods=['GET', 'POST'])