Why does html POST take me to an unexpected page? - python

I have a simple web application built with Python using flask that has three pages: main, index, and post. I am trying to get to the "if request.method == "POST"" section of the index page. To test this I've asked it to render the post.html page. For some reason when I send a POST method from the index page I'm instead being redirected to my main_page. The python code looks like this:
from flask import Flask, redirect, render_template, request, url_for
app = Flask(__name__)
app.config["DEBUG"] = True
#app.route("/", methods=["GET", "POST"])
def index():
if request.method == "GET":
return render_template("main_page.html")
#implied ELSE here (if it's not GET, do the following for a POST)
return redirect(url_for('index'))
#app.route('/index', methods=['GET', 'POST'])
def new_index():
if request.method == "POST":
#I AM TRYING TO GET HERE
return render_template('post.html')
if request.method == "GET":
return render_template('index.html',)
#app.route('/post')
def post():
return render_template('post.html')
The POST method from index.html comes from this:
<div class="row">
<form role="form" method='POST' action='.'>
<textarea class="form-control" name="contents" placeholder="Enter a comment"></textarea>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
I'm not particularly familiar with HTML but I've tried everything I can think of. Any advice?

When you want to link to the same page in a form action you should actually put a question mark, an empty string, a hash or just leave out the attribute all together. Doing either of these will fix your code.
<form role="form" method='POST' action='?'>
<form role="form" method='POST' action='#'>
<form role="form" method='POST' action=''>
<form role="form" method='POST'>
My personal preference is using.
<form role="form" method='POST' action='#'>
This will validate in XHTML and doesn't open up any known attack vectors.

First thing I did was testing your API using Postman, and that all works fine: GET goes to the GET handler, POST goes to the POST handler.
The error I found is in the html form, in particular the action tag: you should point that to the API handler explicitly, and relative to the hostname. So, for example, setting that to:
<form role="form" method='POST' action='/index'>
will actually perform a POST on the /index API of your Flask app.

Related

Flask, Passing User Entered Data Between Views

Problem transferring variables across views I tried using sessions and could not get the connection to work. Say I have two pages, a home and page2. I have a flask app that will take user input from the home and print out input on page2.
For example, if you start my app, you will see this as the home page:
This part works fine, I am able to enter a value.
What I want to happen next, is after you click submit, page2 is generated showing what was just entered:
Whatever string value was entered on home should show up in the highlighted portion.
I have the following app.py file:
from flask import Flask, render_template, request, session
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def home():
stringval = ''
if request.method == 'POST' and 'stringval' in request.form:
stringval = request.form.get('stringval')
session["stringvalue_topass"] = stringval
return render_template('page2.html', stringval = stringval)
return render_template("home.html")
#app.route('/page2', methods=['GET', 'POST'])
def page2():
stringvalue_get = session.get('stringvalue_topass')
return render_template('page2.html', stringvalue_get = stringvalue_get)
if __name__ == '__main__':
app.run(debug=True)
The following home.html:
<!doctype html>
<h1>Enter Value </h1>
<div class="main">
<form class="pure-form" method="POST" action="/page2">
stringval:<br>
<input type="text" name="stringval"><br>
<button type="submit" class="pure-button pure-button-primary" value="Submit">Submit!</button>
</form>
</div>
</body>
And the following page2.html
<!doctype html>
<h1>You have selected </h1>
<div class="main">
{% if stringvalue_get %}
<pre>
{% print(stringvalue_get) %}
</pre>
{% endif %}
</div>
</body>
Okay, there are a few issues here. Firstly, the action attribute of your form in home.html is set to "/page2". This means that when the form is submitted, the POST request is going to the /page2 endpoint rather than to the /home endpoint, where you have written the code for handling the form submission. We can fix this by just deleting the action attribute, as this means the form will post to then endpoint that loaded it - in this case /home.
Secondly, Flask sessions cannot be used without setting a secret key to encrypt the session. This can be done by assigning a value to app.secret_key, like so:
app = Flask(__name__)
app.secret_key = b"my_secret_key"
Finally, instead of passing the string to the template like so: render_template('page2.html', stringval = stringval), (note also that this should be stringval_get = stringval), you can access the session object directly from templates already. So, in all, we can change your application code to:
app.py:
from flask import Flask, render_template, request, session
app = Flask(__name__)
app.secret_key = b"my_secret_key"
#app.route('/', methods=['GET', 'POST'])
def home():
if request.method == 'POST' and 'stringval' in request.form:
session["stringvalue_topass"] = request.form.get('stringval')
return render_template('page2.html')
return render_template("home.html")
#app.route('/page2', methods=['GET', 'POST'])
def page2():
return render_template('page2.html')
And your templates to:
home.html:
<!doctype html>
<h1>Enter Value </h1>
<div class="main">
<form class="pure-form" method="POST">
stringval:<br>
<input type="text" name="stringval"><br>
<button type="submit" class="pure-button pure-button-primary" value="Submit">Submit!</button>
</form>
</div>
</body>
page2.html:
<!doctype html>
<h1>You have selected </h1>
<div class="main">
{% if 'stringvalue_topass' in session %}
<pre>
{% print(session["stringvalue_topass"]) %}
</pre>
{% endif %}
</div>
</body>

Why do I keep getting the 405 error (Method Not Allowed The method is not allowed for the requested URL.) when the POST method is correctly set up?

I am learning Flask and I've incurred into a supposedly beginners error:
Method Not Allowed The method is not allowed for the requested URL.
The thing is that, even though I reduce the code into something really simple, I can't find it. As far as I can see, the POST method is correctly set up (I will use comments to explain it).
The files are:
flaskblog.py
from flask import Flask, render_template, url_for, flash, redirect, request
from forms import RegistrationForm
app = Flask(__name__)
app.config['SECRET_KEY'] = '5791628bb0b13ce0c676dfde280ba245'
#app.route("/")
#app.route('/home', methods=['GET', 'POST']) # As one can see, the POST method is written there.
def home():
form = RegistrationForm()
return render_template('home.html', title='Home', form=form)
if __name__ == '__main__':
app.run(debug=True)
home.html
{% extends "layout.html" %}
{% block content %}
<div class="content-section">
<form method="POST" action=""> # The POST method is written there as well.
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
<div class="form-group">
{{ form.username.label(class="form-control-label") }}
{{ form.username(class="form-control form-control-lg") }}
</div>
</fieldset>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</form>
</div>
{% endblock content %}
forms.py
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
submit = SubmitField('Sign Up')
I get the error when I submit the form, any help would be greatly appreciated!
Thanks!
You're not distinguishing between GET and POST requests appropriately in your home route. Flask defaults the incoming request, from the client, as a GET request since your definitions are off. However, this is an issue because the incoming request is actually a POST request as we can see from your form:
<form method="POST" action=""> # The POST method is written there as well.
Perhaps you could try modifying your home route to handle both POST/GET requests approriately, something along the lines of:
#app.route("/")
#app.route('/home', methods=['GET', 'POST'])
def home():
form = RegistrationForm()
# POST request
if form.validate_on_submit():
return redirect(url_for(<route>)) #route to be rendered if form properly validates
# GET request
return render_template('home.html', title='Home', form=form)
Hopefully that helps!

Python Flask : url_for to pass the variable to function from form template

I tried this approach to pass the form input variable to function via url_for method. But its not working. can anyone check and let me know whats wrong in this code.
Function:
#app.route('/report_data/<year>/<week>', methods = ['POST'])
def report_data(year,week):
print year
print woy
HTML Code :
<html>
<body>
<form action="{{ url_for('report_data', year=n_year, week=n_woy) }}" method="post">
<h3> Enter the details </h3>
Year :
<input type="text" name="n_year"> <br>
<br>
Week :
<input type="text" name="n_woy"> <br>
<br>
<input type="submit" value="Submit"> <br>
</form>
</body>
</html>
Issue:
Getting "None" for both variable.
Firstly, How do you provide year, week values before submitting them in your HTML code ?
Did you render the HTML template in your function? If not, how does your flask function knows that you were seeking from that specific form?
If this is your Flask app code -
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/report_data', methods = ['POST', 'GET'])
def report_data():
if request.method == 'POST':
result = request.form
query_year = result['n_year'].encode('ascii', 'ignore')
query_week = result['n_woy'].encode('ascii', 'ignore')
print(query_year, query_week)
return render_template('so_test.html')
if __name__ == '__main__':
app.run(debug=True)
And opened the URL -
http://127.0.0.1:5000/report_data
And I get this picture.
Once I post the data, I can see this data in my Flask app console.
('2018','08')

Python Flask: clicking button doesn't do anything

I am new to Flask and I'm trying to build a simple web app. Basically what I have on the home page is a text input box and a submit button.
After clicking submit, it shows some result based on the text that was inputted (for now it's hardcoded in the code below) and also 2 buttons (positive/negative) to add the inputted text to a specific file (either with a "positive" or "negative" label).
However, the problem I face is with these 2 buttons: they don't do anything when clicked.
Here is what I have for now:
The Python Flask app
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
#app.route('/process-data', methods=['GET', 'POST'])
def process_data():
if request.method == 'GET':
return render_template('index.html')
if request.method == 'POST':
# get the text from the form that was filled in
input_text = request.form['text']
# if submit button is clicked
if request.form['submit'] == 'Submit':
final_result = 'stackoverflow is the best'
if request.form['submit'] == 'Positive':
f = open('dataset/dataset.tsv', 'a')
f.write(input_text + '\t' + 'positive')
# if negative button is clicked
if request.form['submit'] == 'Negative':
f = open('dataset/dataset.tsv', 'a')
f.write(input_text + '\t' + 'negative')
# show the result on the page
return render_template('index.html', result=final_result, text=input_text)
The index.html file
<!doctype html>
<form action="/process-data" method="post" role="form">
<label for="text">Text:</label>
<input type="text" name="text" placeholder="Input sentence here">
<input type="submit" name="submit" value="Submit">
</form>
{% if result is not none %}
{{ result }}
<h2>Add to dataset</h2>
<form action="/process-data" method="post" role="form">
<label for="add-dataset">This sentence was:</label>
<input type="submit" name="submit" value="Positive">
<input type="submit" name="submit" value="Negative">
</form>
{% endif %}
</html>
Create your / route and have it simply return the index template and nothing else like this:
#app.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
Then assign the other route to another function which will do the processing. I wouldn't call it index, maybe something like process-data, like this:
#app.route('/process-data', methods=['GET', 'POST'])
def process_data():
# put the body of your function here
# ...
# ...
# ...
return render_template('index.html', result=final_result, text=input_text)
Finally, you just need to update your form action accordingly:
<form action="/process-data" method="post" role="form">
Check whether you have set the configuration setting SESSION_COOKIE_SECURE. If you're working in localhost or through an unsecure line and you have set SESSION_COOKIE_SECURE=True then no session cookie will be sent and as such no forms, csrf protection and various other operations will work. Instead use SESSION_COOKIE_SECURE=False under these circumstances.

Flask Input validated before submitted?

I basically want my input to be validated (more than 7 character) before the user hits submit.
I have my input:
<form method="POST" action="/send">
<div>
<input type="text" name="input">
</div>
<input type="submit"></form>
So to process it in my flask I thought I would do something like this:
#app.route('/', methods=['GET', 'POST'])
def home():
valid = True
if len(request.form['input']) < 7:
valid = False
flash('Input too short.')
if request.method == 'POST' and valid:
variable = request.form['input']
return render_template('simple.html')
This returns me "Bad Request
The browser (or proxy) sent a request that this server could not understand."
How can I fix this?
Change the action in the form post from action="/send" to action="". Thus, the new line should be:
<form method="POST" action="">
FLASK is not supposed to handle this. WSGI applications do not handle website input dynamically, so the form would have to be submitted before FLASK could deal with it.
What you want is to use regular expressions in a pattern on the element:
<input type="text" pattern=".{8,}" title="Eight or more characters">

Categories