Method Not Allowed in Flask for IntegerField form - python

I'm trying to set up integer field validation on a form in Flask, but I keep getting 'Method Not Allowed'. I can't see what is wrong with my routes?
My main file is:
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import IntegerField
# from perfect_numbers import classify
app = Flask(__name__)
app.config['SECRET_KEY'] = 'MySecretKey'
# num = 12
# Classify = classify(num)
class PerfectForm(FlaskForm):
number = IntegerField(4)
#app.route('/', methods=['POST'])
def form():
form = PerfectForm()
return render_template('index.html', form = form)
if __name__ == '__main__':
app.run(debug=True)
Then I have a standard layout.html file:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>PERFECT App</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
</head>
<body>
{% block body %}{% endblock %}
</body>
</html>
And my index.html is:
{% extends 'layout.html' %}
{% block body %}
<h1>PERFECT NUMBERS</h1>
<p>This is a basic Flask App to illustrate Aliquot Sums and Perfect Numbers</p>
<p>Input a number to check if it is abundant, perfect, or deficient</p>
<form action="{{ url_for('/') }}" method="POST">
{{ form.csrf_token }}
{{ form.number }}
</form>
<!-- {{ num }} is {{ classify }} -->
{% endblock %}

First, change the app.route() signature to
#app.route('/', methods=['POST', 'GET'])
You have to fill the form and submit for your app to receive a POST request. When you first load the page on your browser, you technically send a GET request to flask, and it returns the page.
To handle your POST request separately, do something like this :
#app.route('/', methods=['POST', 'GET'])
def form():
if request.method == 'POST':
# Do something with request (access form elements with request.get(key))
return 'Somehing here'
form = PerfectForm()
return render_template('index.html', form = form)

Related

Request form leads to error 405 Method Not Allowed in Flask

I am trying to set up a flask app which logs the user in and then prompts them with the segmentation page. The text box has then to be filled in by the user and upon submitting it, the text gets processed. Finally, the user should be logged out automatically.
However, I get a '405 Method Not Allowed' error when reaching the segmentation page.
from flask import Flask, jsonify, redirect, render_template, request, Response, session, url_for
app = Flask(__name__)
app.secret_key = 'super secret key'
#app.route('/')
def home():
"""
Sets up home page.
"""
return redirect(url_for('login'))
#app.route("/login", methods = ['POST', 'GET'])
def login():
"""
Login page to retrieve
"""
if request.method == 'POST':
user = request.form['nm']
session['user'] = user
return redirect(url_for("my_form_post"))
else:
if 'user' in session:
return redirect(url_for('my_form_post'))
return render_template('login.html')
#app.route('/segmentation')
def my_form_post(methods = ['GET','POST']):
"""
Sets up input text box and executes relevant action.
"""
if 'user' in session:
render_template('segment.html')
user = session['user']
if request.method == 'POST':
text = request.form['text']
# This function has no return argument
save_in_directory(text)
return redirect(url_for('logout'))
else:
return render_template('segment.html')
else:
return render_template('login.html')
#app.route("/logout")
def logout():
session.pop('user', None)
return redirect(url_for('submitted'))
#app.route("/sent")
def submitted():
return render_template('success.html')
if __name__ == '__main__':
app.run(debug = True)
The segment.html template is as follows:
{% extends "base.html"%}
{% block title%}Login Page{% endblock%}
{% block content%}
<body>
<p style="font-family: Helvetica; margin-left: 1.2em;"><b>Please, introduce the path:</b></p>
</body>
<form method="POST" style="font-family: Helvetica; font-style:italic; margin-left: 1.2em;" action="/segmentation">
<input name="text" size = 150>
<input type="submit">
</form>
<p style="font-size:1px;line-height:1;"><br/></p>
{% endblock %}
where base.html is:
<!doctype html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<link rel="shortcut icon" href="{{url_for('static', filename='heart.PNG')}}" type="image/png">
<img src="{{url_for('static', filename='welcome.png')}}" style='height: 100%; width: 100%; object-fit: cover'/>
{% block content%}
{% endblock %}
</html>
You're defining the methods for your /segmentation route as a function parameter which is incorrect, instead you have to define methods on route decorator like this:
#app.route('/segmentation', methods = ['GET','POST'])
def my_form_post():
...

Flask Preventing Form Injection

How can python / flask block foreign form injections?
Consider the following mwe:
app.py
from flask import Flask, request, render template
app = Flask(__name__)
#app.route('/', methods=['GET','POST'])
def helloworld():
if request.method == 'GET':
return render_template('index.html')
if request.method == 'POST':
print(request.form['info'])
## do something with the info, like write to a database
return 'nothing'
if __name__ == '__main__':
app.run(debug=True)
templates/index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type='text/javascript' src="{{ url_for('static', filename='js/fire.js') }}"></script>
</head>
<body>
<p>Hello world!</p>
</body>
</html>
static/js/fire.js
$(document).click(function() {
// post data to flask
$.post('/', {'info': 'test'});
return false;
};
My questions are:
Is injection possible from a foreign website? Follow-up: how could this be done? (e.g., perhaps via a form that posts to my website url?)
If injection is possible, what can I do in the app.py script to block the injection?
Edit
Here is a very basic script that can be used to test injections against the above flask application. The accepted answer blocks this script:
<!DOCTYPE html>
<html>
<body>
<h2>Malicious Form Injection</h2>
<form action='http://127.0.0.1:5000/' method='post'>
Input 1:<br>
<input name="info" value="mal1"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
app.py
from flask import Flask, request, render template
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
CSRFProtect(app)
app.config['SECRET_KEY'] = 'somethignrandom'
#app.route('/', methods=['GET','POST'])
def helloworld():
if request.method == 'GET':
return render_template('index.html')
if request.method == 'POST': # anything post will autocheck csrf
print(request.form['info'])
## do something with the info, like write to a database
return 'nothing'
if __name__ == '__main__':
app.run(debug=True)
There is no need to pass the secret key to the html template, as CSRFProtect will automatically pass the secret key.
templates/index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<meta name='csrf-token' content="{{ csrf_token() }}">
<script type='text/javascript' src="{{ url_for('static', filename='js/fire.js') }}"></script>
</head>
<body>
<p>Hello world!</p>
</body>
</html>
script.js
$(document).click(function() {
// post data to flask
$.post('/', {'info': 'test', '_csrf_token':$('meta[name="csrf-token"]').attr('content')});
return false;
};

Python Flask Error "missing required positional arguments" (even though they were supplied)

I have an flask index route that is redirecting to another route like this:
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
filepath="some/path/"
top_1 = "something"
top_2 = "somethingelse"
top_3 = "somethingelse"
return redirect(url_for('display_preds', filepath=filepath,
top_1=top_1, top_2=top_2, top_3=top_3),
code=307)
And "display_preds" looks like this:
#app.route('/display_preds', methods=['GET', 'POST'])
def display_preds(filepath, top_1, top_2, top_3):
if request.method == 'POST':
return render_template("prediction.html")
finally in the "prediction.html" page I have this:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test page</title>
</head>
<body>
<h1>This is just a test</h1>
<img src="{{ filepath }}" alt="some text">
<br>
<p> {{ top_1 }} </p> <br>
<p> {{ top_2 }} </p> <br>
<p> {{ top_3 }} </p>
</body>
</html>
After executing this I can see that the redirect() has values for "filepath", "top_1" etc. because I see it trying to POST something like this:
address/display_preds?filepath=%2some%2file%2path&top_2=something&top_1=somethingelse&top_3=somethingelse
Why am I getting an error that says:
TypeError: display_preds() missing 4 required positional arguments: 'filepath', 'top_1', 'top_2', and 'top_3'
UPDATE:
I tried changing the route to #app.route('/display_preds/<filepath>/<top_1>/<top_2>/<top_3>/', methods=['GET','POST']) but that just changes the error to this:
werkzeug.routing.BuildError: Could not build url for endpoint 'display_preds/filepath/top_1/top_2/top_3' with values ['filepath', 'top_1', 'top_2', 'top_3']. Did you mean 'display_preds' instead?
Where are you providing the values for render_template?
You can either use a context dict like
context = {"filepath": filepath, "top_1": top_1, "top_2":top_2, "top_3": top_3}
render_template("prediction.html", context=context)
or directly in render_template with:
render_template("prediction.html", filepath=filepath, top_1=top_1, top_2=top_2, top_3=top3)
UPDATE: Your redirect is using a GET request to the new url, but in your endpoint, you are not retrieving the parameters with request.args.get("top_1").
def display_preds():
filepath = request.args.get("filepath")
top_1 = request.args.get("top_1")
....

use python pandas to show csv to placeholder in web file

I have a csv file and need to convert it to view it in html. I know python pandas can do it with
df = pd.read_csv("myfile.csv")
df.to_html('output.html')'
but I don't want to show it in single web page, I have index.html file and I want to show it in there but in another section.
found the answer using tablib
from flask import Flask, render_template
import tablib
import os
app = Flask (__name__)
dataset = tablib.Dataset()
with open(os.path.join(os.path.dirname(__file__),'email.csv')) as f:
dataset.csv = f.read()
#app.route("/")
def index():
data = dataset.html
#return dataset.html
return render_template('index.html', data=data)
if __name__ == "__main__":
app.run()
index.html
<html>
<head>
<meta charset="utf-8" />
<link rel=stylesheet type=text/css href="{{ url_for('static',
filename='css/style.css') }}"/>
<title>Show CSV</title>
</head>
<body>
<div class="table">
{% block body %}
{{ data|safe }}
{% endblock %}
</div>
</body>
</html>

werkzeug.routing.BuildError with Flask Python

**difference to the suggested repeat, my error stemmed from the following line being missing in the original code session['message']=request.form['message'] wherease in the suggested duplicate was missing the render_template component`
I am trying to create user sessions with Flask, I don't care about authentication. I just want a page where they enter their name, and then they are redirected to the main page. I tried to follow the example in this link here but I get a werkzeug.routing.BuildError. To summarise my python app is:
from flask import Flask, render_template
from flask import request, session, url_for,abort,redirect
app = Flask(__name__)
app.config['SECRET_KEY'] = 'F34TF$($e34D';
#app.route('/')
def home():
return render_template('index.html')
#app.route('/signup', methods=['POST'])
def signup():
session['username'] = request.form['username']
session['message']=request.form['message']
return redirect(url_for('message'))
#app.route("/message")
def message():
return render_template("message.html")
if __name__ == '__main__':
app.run(debug=True)
and index.html is:
{% extends "layout.html" %}
{% block content %}
<h1>Say something</h1>
<form method="post" action="{{ url_for('signup') }}">
<p><label>Username:</label> <input type="text" name="username" required></p>
<p><button type="submit">Send</button></p>
</form>
{% endblock %}
layout.html is:
<!doctype html>
<html lang="en">
<head>
<title>Say somthing</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
You are getting that error because you don't have a route called message and yet you are redirecting to it.
#app.route('/signup', methods=['POST'])
def signup():
session['username'] = request.form['username']
# Create a message route first
return redirect(url_for('message'))
Here's a sample route called message
#app.route("/message")
def message():
return render_template("message.html")

Categories

Resources