Flask - POST - The method is not allowed for the requested URL - python

I just started learning Flask but I meet troubles with the POST method.
Here is my (very simple) Python code :
#app.route('/test')
def test(methods=["GET","POST"]):
if request.method=='GET':
return('<form action="/test" method="post"><input type="submit" value="Send" /></form>')
elif request.method=='POST':
return "OK this is a post method"
else:
return("ok")
when going to :
http://127.0.0.1:5000/test
I successfully can submit my form by clicking on the send button but I a 405 error is returned :
Method Not Allowed
The method is not allowed for the requested URL.
It is a pretty simple case, but I cannot understand where is my mistake.

You gotta add "POST" in the route declaration accepted methods. You've put it in the function.
#app.route('/test', methods=['GET', 'POST'])
def test():
if request.method=='GET':
return('<form action="/test" method="post"><input type="submit" value="Send" /></form>')
elif request.method=='POST':
return "OK this is a post method"
else:
return("ok")
See : http://flask.pocoo.org/docs/0.10/quickstart/

Related

'Method Not Allowed: The method is not allowed for the requested URL.' when submitting text entry

I am running flask python along with HTML, and the problem is simple:
It gives me error 405: Method Not Allowed: The method is not allowed for the requested URL. I have looked this up and people say to include methods=['GET', 'POST'] in the page route, but I already have this.
Python code:
#app.route('/')
#app.route('/home/', methods=['GET', 'POST'])
def home():
global count
guess = request.form.get('guess')
result, valid = guess_word(guess, word, words)
print(result, valid)
try:
guesses[count-1][2] = 'p'
except:
guesses[count-1][2] = ''
if count < 6:
if valid:
guesses[count][0] = guess
guesses[count][1] = result
session['guesses'] = guesses
if valid:
count += 1
return render_template('index.html', guesses=session['guesses'])
HTML code:
<div class="container2">
<form method="post">
<input type="text" placeholder="Guess" class="text-input" name="guess">
</form>
</div>
This worked before, and I changed (what I thought was) nothing, but it suddenly stopped working. It gives me the error when I submit the text entry.
Your form has no action attribute i.e. you have not explicitly said where your data should go to when you submit your form. In the absence of an action attribute, it will assume your home page which is / and from your routing, this does not support POST.
2 possible solutions
Add the action attribute to your form
<form action ="home" method ="post">
Keep your data as is and add support for POST to your route for / i.e. change your routing code to
#app.route('/', methods=['GET', 'POST'])
#app.route('/home/', methods=['GET', 'POST'])

How can I create a session from user input?

Disclaimer: Yes I know this might not be the right way to do it, but I just need a quick and easy fix and in this case I prefer the quick and dirty way.
I have a login.html like this:
<form method="POST" class="login-form" action="/login">
<input type="text" name="code">
<input type="submit" value="Submit">
</form>
I have two types of users who will visit this page. First type of users will have "code_A", second type of users will have "code_B". Depending on which code they will type into the form field they will be redirected to gallery or gallery2.
This is my routes.py:
code_A = "code_A"
code_B = "code_B"
#app.route("/login", methods=['GET', 'POST'])
def login():
if 'code_A' in session:
redirect(url_for('gallery'))
elif 'code_B' in session:
redirect(url_for('gallery2'))
elif request.method == 'POST':
code_post = request.form['code']
if code_post == code_A:
session['code_A'] = code_A
return redirect(url_for('gallery'))
elif code_post == code_B:
session['code_B'] = code_B
return redirect(url_for('gallery2'))
else:
return render_template("login.html")
else:
return render_template("login.html")
return render_template("login.html")
#app.route("/gallery")
def gallery():
if 'code_A' not in session:
return redirect(url_for('login'))
else:
return render_template('gallery.html')
#app.route("/gallery2")
def gallery2():
if 'code_B' not in session:
return redirect(url_for('login'))
else:
return render_template('gallery2.html')
I though I could simply take the input from the form and create a session based on the input. If the right code is in the session, the user should be redirected to gallery or gallery2, depending on the session. But with this code I always get redirected to login.html and no session is created. How do I pass the input from the form to the routes.py and create a session from it. I mean it cannot be that hard, or is it?
I am thankful for every suggestion, because I am loosing my mind over it. Thanks and best regards!

BadRequestKeyError [duplicate]

This question already has answers here:
Get the data received in a Flask request
(23 answers)
Closed 4 years ago.
Multiple questions have been asked with a similar error on SO. I have tried all of the solutions but still keep getting a beautiful error:
werkzeug.exceptions.HTTPException.wrap..newcls: 400 Bad Request: KeyError: 'username'
Below is my html form:
<form action='/login' method = "GET">
<label>Name: </label>
<input name="username" type="text">
<input type="submit" name='submit' value='submit'>
</form>
Here is the function that deals with the form data:
#app.route('/login', methods = ['GET'])
def login():
if request.method == "GET":
un = request.form['username']
return un
I have learned Bottle and transitioning towards Flask.
So far, I have tried the following:
1) GET to POST
2) request.form.get('username', None)
3) Add POST reason to the function route without any rhyme or reason.
Can somebody help me out?
You need to change the method (GET to POST) in the html file and add the method in the decorator #app.route too. Do not forget that in this case a return (GET) is required to render the html file.
#app.route("/login", methods = ['GET', 'POST'])
def login():
if request.method == "POST":
# import pdb; pdb.set_trace()
un = request.form.get('username', None)
return un
return render_template('form.html')
Tip: Learn about pdb to debug your programs more easily.
https://realpython.com/python-debugging-pdb/
https://docs.python.org/3/library/pdb.html
no need to change your form action method. but when you use GET method for sending form data your data is transmitted as URL variables and you need to read data this way:
if flask.request.method == 'GET':
username = flask.request.args.get('username')
and if you change the method to POST, read content as below:
if flask.request.method == 'POST':
username = flask.request.values.get('username')

Problems redirecting to dynamic URLs in Flask with 'action'

My app has a text box and a submission button. I am trying to create and redirect to dynamic URLs in my app, that are based off of what is typed in the text box. For example, the user enters in '1234', clicks submit, then is taken to 'website.com/results/1234'. The problem seems to be that the HTML for my button doesn't want to redirect the user to the new, dynamic URL. I am passing this to the HTML with Jinja.
Here is what I have.
The user starts on the home page, that is defined like this
#app.route("/home/", methods=["GET", "POST"])
def start():
return render_template("dashboard.html")
Dashboard.html has a text box and submission button (below). As you can see, the action of this button is to redirect to {{ results_page }}, where "results_page" comes from my Python function load_results (also below) and is passed to the HTML with render_template.
<div>
<form action="{{ results_page }}" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" placeholder="18952863, 18269575" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
</div>
The results page of my app uses the user input to look up some information and display it.
#app.route('/results/<query>', methods=["GET", "POST"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'POST':
query = form.pmid.data #This is the user input
results_page = "website.com/results/"+query
return(query)
#do lots of stuff using the query
return render_template('results.html', form=form, results_page = results_page)
except Exception as e:
return(str(e))
If I run my app like this, the home page is fine, but when I click "Submit", the app doesn't take in the user input or do anything with it. Simply the home page refreshes.
I am new to web development, but since this code works fine if I hardcode the button to action = "website.com/results" in the HTML, AND do without the <query> in /results/<query> for the results page, I think that only a few adjustments should be needed to make my app dynamically redirect to and load pages correctly. Right now, I'm not sure what I'm missing. Please let me know where I'm going stray.
EDIT -
Now I have implemented a handle_form function that redirects to my dynamic URL. This function properly redirects but then I get a 404 error.
#app.route('/form/', methods=["POST"]) #handles form submission
def handle_form():
form = pmidForm(secret_key='super secret key')
if request.method == "POST":
query = request.form['pmid']
return redirect('/results/'+query)
I have also edited my form in the HTML action to go to /form/
<form action="website.com/form/" class="form-inline" method="post">
<div class="form-group">
<label for="PubmedID">Pubmed ID(s)</label>
<input type="text" class="form-control" id="PubmedID" placeholder="18952863, 18269575" name="pmid" value="{{request.form.pmid}}">
</div>
<button type="submit" id= "myButton" class="btn btn-default" data-toggle="modal" data-target="#myModal">Submit</button>
</form>
With this, my site will properly redirect to /results/<query>/ (e.g. /results/1234) but I get a 404 error. Here is how I have changed my load_results
#app.route('/results/<query>', methods=["GET"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'GET':
query = form.pmid.data #THIS IS THE USER INPUT FROM THE FORM #referencing 'class pmidForm'
return query
.
.
#do stuff
I think I am very close but need to figure out why I am getting a 404 error. My guess is that I am using "GET" incorrectly. My form in the HTML uses method="post". Since this does not match with "GET", is there no way for my load_results(query) function to retrieve the contents of the form?
EDIT 2 -
Changed handle_form to redirect with url_for:
#app.route('/form/', methods=["POST"]) #handles form submission
def handle_form():
form = pmidForm(secret_key='super secret key')
if request.method == "POST":
query = request.form['pmid']
return redirect(url_for('.load_results', query=query))
And changed load_results to not return "query"
#app.route('/results/<query>', methods=["GET"])
def load_results(query):
form = pmidForm(secret_key='super secret key')
try:
if request.method == 'GET':
query = form.pmid.data # This shouldn't work??
.
.
# do stuff with the variable "query"
With this, it's still returning the 404 Error as before. Should I not be using if request.method == GET ?
Edit 3 -
Even a very simplified load_results will give me the 404 error, so I'm not sure what's up.
#app.route('/results/<query>', methods=["GET", "POST"])
def load_results(query):
q = query
return render_template('test.html', q=q)
EDIT - 3
It seems that the accepted solution IS the correct solution to this problem, however there is an issue with uwsgi in my server that is re-directing it to the wrong location :(
Your punctual problem is that /home route function also needs to put the results_page url on the templating context.
results_page = "website.com/results"
return render_template("dashboard.html", results_page=results_page)
Since the variable is undefined, flask is calling the original endpoint on the form submission.
Your larger problem is that your appraoch isn't going to get you a dynamic results url that looks like /results/1234.
Typical approaches are to redirect on the server side when you handle the post request; or to use JavaScript in the client to get the form data and change the browser location to /results/1234.
A simplified version of how to handle this with a server side redirect might look something like this. One route that handles the form submission and another that displays results. You simply redirect from one to the other to get the nice url.
#app.route('/form', methods=["POST"])
def handle_form():
query = form.pmid.data #This is the user input
return redirect(url_for('.load_results', query=query))
#app.route('/results/<query>') *removed the method spec to handle the redirect?
def load_results(query):
.
.
# do something with query

How to redirect from helper function in Flask?

On many forms we have a cancel button. Trying to keep things DRY it seemed that a helper function was in order. The function if_form_cancel() would check to see if the cancel button had been clicked and then redirect accordingly (with destination passed to the function). However, the redirect is ignored when we use a helper function (it works just fine in-line).
view.py
from helper.py import if_form_cancel
...
def index():
return render_template('index.html')
def edit():
...
form = EditForm(obj=user, csrf_enabled=False)
if request.method == 'POST':
if_form_cancel(url_for('.index'))
if form.validate():
...
return redirect(url_for('.index'))
return render_template('edit.html')
helper.py
from flask import request, redirect, flash
def if_form_cancel(destination='/'):
try:
if request.form['cancel']:
flash(u'Operation canceled at user request')
return redirect(destination)
except KeyError:
pass
edit.html
....
<input type="submit" name="submit" value="Submit" />
<input type="submit" name="cancel" value="Cancel" />
....
Result if cancel button is clicked: helper function is called, correct message is flashed, but redirect does not occur. Is it possible to redirect from a helper function like this? If so how? What are we doing wrong?
You aren't returning the result of if_form_cancel in the edit function. But you only want to return if the user cancelled. An exception would work well here, and it looks like werkzeug has such an exception.
See if this will work (untested).
from werkzeug.routing import RequestRedirect
def if_form_cancel(destination='/'):
try:
if request.form['cancel']:
flash(u'Operation canceled at user request')
raise RequestRedirect(destination)
except KeyError:
pass
Alternatively, you could just have the form_cancel helper check the form and return True if the user cancelled, and do the redirect in the view itself.
def edit():
...
form = EditForm(obj=user, csrf_enabled=False)
if request.method == 'POST':
if form_cancel():
return redirect(url_for('.index'))
if form.validate():
...
return redirect(url_for('.index'))
return render_template('edit.html')
Also, consider using validate_on_submit instead of checking for POST manually.
https://flask-wtf.readthedocs.org/en/latest/quickstart.html#validating-forms

Categories

Resources