Flask - 400 Bad request - python

I have this python code which should take in data from an html form and use it in a WHERE clause:
#app.route('/search', methods=['GET'])
def search():
connect = cx_Oracle.connect("benjamin", "siliyetu", "PRINCE-PC/XE")
cursor = connect.cursor()
searched = request.form['search']
named_params = {'search':searched}
query = cursor.execute("SELECT * FROM edited WHERE REGEXP_LIKE (cod_ed,
:search) OR REGEXP_LIKE (nome_ed,:search) OR
REGEXP_LIKE (endereco,:search) OR REGEXP_LIKE
(telefone,:search) OR REGEXP_LIKE
(cidade,:search)", named_params)
results = cursor.fetchall()
posts = list(results)
return render_template('search.html', posts=posts)
and the template I'm using is this(part of the template anyway. Its not the whole thing):
<form method="POST" action="/editora" class="form-outline" >
<div class="col-lg-7 col-offset-6 right">
<div class="form-group mx-lg-3 mb-2">
<label for="element-7" ></label>
<input id="search" name="search" type="text" class="form-control" placeholder="Pesquisar..." />
<label></label>
<a class="btn btn-primary " type="submit" href="search">Pesquisa</a>
When I try to use the data from the form, it gives me a
werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400 Bad Request: KeyError: 'search'
But when I input data using input() it works fine. What gives!?
How would I go about fixing this issue? I also want to add some regular expressions in the where clause but its not budging. How do I do that too?
Ps- I'm working with oracle express edition 11g

Without having a traceback (you are running with the debug server while developing, right?), the exception you're getting comes from the
searched = request.form['search']
line.
the HTML example you have POSTs to /editora
the Python code you have has a route /search (not /editora) and the view won't accept POST requests anyway (methods=['GET']).
Are you sure the snippets you've posted are correct?
request.form is only populated for POST requests, anyway.

If you want to submit data to search route, you form action should point to that route.
<form method="POST" action="/search" class="form-outline" >
And if you want for a search route to get that data from POST request, you should put in methods 'POST' value.
#app.route('/search', methods=['GET', 'POST'])
The reason why you get:
werkzeug.exceptions.HTTPException.wrap.<locals>.newcls: 400 Bad Request: KeyError: 'search'
because you are sending something to the application or server that they cannot handle. (http://werkzeug.pocoo.org/docs/0.14/exceptions/#werkzeug.exceptions.BadRequest)
KeyError is for trying to access dict object parameter 'search' which doesn't exist beucase form is never submited.

Related

Flask - Creating dynamic URL based on user input

I'm trying to create a dynamic URL based on user's input from an HTML form.
For example, if a user types in 'AAPL' or 'KO', the next page should be:
webapp.com/result/AAPL or webapp.com/result/KO
index.html:
<div class="cell" id="cell-1-2">
<span class="data" style="text-align: center;">
<form action="{{ url_for('ticker_result', variable='variable') }}" method="POST">
<input type="text" name="variable" placeholder="search ticker or company" maxlength="4"
font-size="24px" style="text-transform:uppercase">
<input class="button" type="submit" value="Search" onclick="tickerSymbol();">
</form>
</span>
</div>
I've tried renaming the 'variable' part to several different things and nothing works so I'm just stuck at this point.
main.py:
# Routing to the homepage
#app.route("/")
def markert_hours_today():
return render_template(
"index.html")
# Routing to the result page
#app.route("/result/<variable>", methods = ["GET", "POST"])
def ticker_result(variable):
if request.method == "POST":
result = request.form["variable"]
return render_template(
"result.html",
result=result)
When I run the local environment, and type in a ticker symbol, the next URL is:
webapp.com/result/variable
I'm assuming it's HTML that I need to edit? I've read the quickstart documentation for Flask which isn't making sense to me and looked up similar questions but I can't seem to find an answer to this.
You are coming at it from the wrong way. When Jinja creates index.html based on your template you can't know what variable is going to be, so how is Jinja supposed to know?
What you need to do is send the form back to the same route and then redirect the user to whatever they typed in. So something like this:
from flask import request, redirect, url_for
#app.route("/", methods=['GET', 'POST'])
def markert_hours_today():
if request.method == 'POST':
result = request.form['variable']
return redirect(url_for('ticker_result', variable=result)
return render_template("index.html")
Edit: forgot to rename variable from user_input to result.

Python Flask - Processing input obtained from front-end in the back-end

this is more of a question about where to find how I would do this rather than asking how to do this. I'm sure this is well covered, I'm just struggling to articulate the correct term which I could use to Google and find out the answer.
Anyway - I have a Python Flask web application. On a web page, there is an input box which requests user input. What I would like to happen, is for some magic to happen with the user input in the background. In my own scenario, I would like to take a URL, then use bs4 to pick off what I need and display this on the web page.
For simplicity, I'll ask for something for simple and I can then build on it from there: if I were to request the user to specify a number then press 'Submit', how could I multiply the number by 10?
If my code for the form was index.html:
<form class="form-horizontal" role="form" method="post" action="/">
{{ form.csrf_token }}
<fieldset>
<div class="form-group">
<label for="GetNum" class="col-lg-2 control-label">Enter Number</label>
<div class="col-lg-6">
<input type="text" id="GetNum" name="GetNum" class="form-control" value="">
</div>
<input class="btn btn-success" type="submit" value="Calculate">
</div>
</fieldset>
</form>
I noticed that I can get the input to print to a paragraph by <p>form.request.GetNum</p>.
Now for this question's example, the code for the backend functionality will be:
import math
GetNum = 10 #should be form.request.GetNum value
CalcResult = GetNum*1000
print CalcResult # or {{ CalcResult.data }} or something in index.html
My Controller (app.py) looks like:
#app.route('/', methods=['GET', 'POST'])
#login_required
def home():
error = None
form = PostForm(request.form) # unrelated to question, but will this clash?
if .. :
do something
return redirect(..)
else:
do something else..
return render_template(..)
My worry is that the home function will end up having a mass of code if I have to put the math function in there. So tl;dr, how would I implement a back end function within my code? (or please provide me with material to read, thank you!)
One other thing is I already have a form on my '/' page, will I have to rename the forms like form1 form2 because they will clash or will it be OK?
Elsewhere in your code base, either in the same file, or more likely
a module or package you could define that complicated task. Lets
create a simple module complicated.py in the same directory as your
other code, that then defines the complicated task:
def do_really_complicated_thing(info):
# lots of complicated steps
return really_complicated_data
Then in our view code, we can just use that instead of having it embedded:
from complicated import do_really_complicated_thing
#app.route('/', methods=['GET', 'POST'])
#login_required
def home():
error = None
form = PostForm(request.form)
if form.validate_on_submit() :
info = form.info.data
complicated_task = do_really_complicated_thing(info)
return render_template('something', results=complicated_task)
So, in short-- the terms you're looking for is packages and modules, they
help your code be neater and reusable.
As for clashing forms— you can just target the form to post to a specific route which just handles that form, which is much cleaner then then having to validate/parse different forms in a single route.

How to unit test a form submission when multiple forms on a route?

I want to add unit tests to my flask app that tests form behavior on valid and invalid logins + signups. Currently, I have the signup form and a login form hosted on one page and route, and am using a hidden input field to identify which of the two forms is submitted / determine next actions.
My question is - how do I write a unit test that targets a specific form on a page? All the examples I've seen so far post data to a specific route, which is currently what I am doing. But that is failing because I need an added way to say "and we're submitting x form".
So is there a way to add "and we're submitting x form" in the post request?
**
edited to add, here are the different ways I've tried to pass the hidden form data in the post data dict, with no success.
data = dict(username="test#gmail.com", password="test", login_form)
data = dict(username="test#gmail.com", password="test", "login_form")
data = dict(username="test#gmail.com", password="test", login_form=True)
login unit test:
from app import app
import unittest
class FlaskTestCase(unittest.TestCase):
#ensure that login works with correct credentials
def test_correct_login(self):
tester = app.test_client(self)
response = tester.post(
'/login',
data = dict(username="test#gmail.com", password="test"),
follow_redirects=True
)
self.assertIn(b'you are logged in', response.data)
login route in views.py:
#app.route('/login', methods=['POST', 'GET'])
def login():
login_form = LoginForm()
signup_form = SignupForm()
error_login = ''
error_signup = ''
#login form
if 'login_form' in request.form and login_form.validate():
# do login form stuff
#signup form
if 'signup_form' in request.form and signup_form.validate():
# do signup form stuff
return render_template('login.html', login_form=login_form, signup_form=signup_form, error=error)
login.html:
<div class="login-form form-400">
<h3>Log In To Your Account</h3>
<form action="" method="post">
<input type="hidden" name="login_form">
{% if error_login != '' %}
<label class="error">
{{ error_login }}
</label>
{% endif %}
{% from "_formhelper.html" import render_field %}
{{ login_form.hidden_tag() }}
{{ render_field(login_form.email, placeholder="Your Email", class="form-item__full", type="email") }}
{{ render_field(login_form.password, placeholder="Your Password", class="form-item__full") }}
<input type="submit" value="Login" class="button button-blue">
</form>
</div>
<p class="login-divider">or</p>
<div class="signup-form form-400">
<h3>Create a New Account</h3>
<form action="" method="post">
<input type="hidden" name="signup_form">
{% if error_signup != '' %}
<label class="error">
{{ error_signup | safe}}
</label>
{% endif %}
{% from "_formhelper.html" import render_field %}
{{ signup_form.hidden_tag() }}
{{ render_field(signup_form.username, placeholder="Pick a Username", class="form-item__full") }}
{{ render_field(signup_form.email, placeholder="Your Email", class="form-item__full", type="email") }}
{{ render_field(signup_form.password, placeholder="Create a Password", class="form-item__full") }}
<input type="submit" value="Sign Up" class="button button-green">
</form>
</div>
Ok I figured it out. To pass the login_form info, I had to end up passing an empty string on the login_form like this:
def test_correct_login(self):
tester = app.test_client(self)
response = tester.post(
'/login',
data = dict(username="test#gmail.com", password="test", login_form=""),
follow_redirects=True
)
self.assertIn(b'you are logged in', response.data)
I did this by throwing a print request.form in my views.py for this route and then saw the empty string.
It was still failing, but because the login_form.validate() was failing because of the csrf token added by the WTForms module. In the end, this discussion had the answer.
Flask-WTF / WTForms with Unittest fails validation, but works without Unittest
Thanks #drdrez for your suggestions!
Update:
Thanks for updating your question with what you've already tried! I have a few other ideas about what's causing the issue.
Let's continue to look at the HTML and try to understand the technologies your program is built on top of. In the server side login.html file, notice these lines:
{% from "_formhelper.html" import render_field %}
{{ login_form.hidden_tag() }}
{{ render_field(login_form.email, placeholder="Your Email", class="form-item__full", type="email") }}
{{ render_field(login_form.password, placeholder="Your Password", class="form-item__full") }}
It isn't HTML, and is probably being processed on the server side to produce HTML and serve to the client. The line that contains login_form.hidden_tag() looks interesting, so I would recommend loading this page in your browser and inspecting the HTML served to the client. Unfortunately, I haven't used Flask before, so I can't give any more direct help.
However, my advice is to continue digging into how Flask and the HTML Form works. The nice thing about Python is you have access to libraries' source code, which allows you to figure out how they work so you can learn how to use them and fix bugs in your application that uses them.
Sorry I can't give you more direct help, good luck!
Let's look at login.html. When you submit a form, how does the login route in views.py know which form was submitted? If you know HTML Forms, <input> elements nested in a form are used to, in this case, post data to your server/application.
Back to login.html, notice these two lines:
...
<h3>Log In To Your Account</h3>
<input type="hidden" name="login_form">
...
<h3>Create a New Account</h3>
<form action="" method="post">
<input type="hidden" name="signup_form">
...
Those are <input> elements, with a type of "hidden", so they won't display, with names of "login_form" and "signup_form", which are included in the data that is submitted by the form.
Now in the login route in views.py, you'll notice there two lines:
#login form
if 'login_form' in request.form and login_form.validate():
# do login form stuff
#signup form
if 'signup_form' in request.form and signup_form.validate():
# do signup form stuff
Those are testing to see if the phrase "login_form" or "signup_form" are in present in the list request.form. Back to your unit test now:
response = tester.post(
'/login',
data = dict(username="test#gmail.com", password="test"),
follow_redirects=True
)
Notice the data you are passing in the dict, this is mimicking the form data, so you should probably include either "login_form" or "signup_form" to mimic the behavior of the HTML form correctly.
If you're unfamiliar with HTML Forms and HTTP Post, I would suggest searching for some tutorials, or just reading documentation on MDN or elsewhere. When building software on top of a technology (like HTTP and HTML), it can be helpful to understand how those technologies work when you run into bugs in your own software.
Hope this helps, let me know if I can clarify anything!
You might be experiencing a problem because you have not flagged the request as being of the form application content type. I note you are trying to access request.form, which requires that the data package is parsed in a certain way. You could try to do something like the following:
response = tester.post(
'/login',
data = dict(username="test#gmail.com", password="test"),
follow_redirects=True,
headers = {"Content-Type":"application/x-www-form-urlencoded"}
)

Deleting rows from database with python flask?

I am using a flask framework, and can't seem to delete rows from the database. The code below gives a 405 error: "The method is not allowed for the requested URL." Any ideas?
In the py:
#app.route('/delete/<postID>', methods=['POST'])
def delete_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('delete from entries WHERE id = ?', [postID])
flash('Entry was deleted')
return redirect(url_for('show_entries', post=post))
In the html:
<h3>delete</h3>
Clicking <a href...>delete</a> will issue a GET request, and your delete_entry method only responds to POST.
You need to either 1. replace the link with a form & submit button or 2. have the link submit a hidden form with JavaScript.
Here's how to do 1:
<form action="/delete/{{ entry.id }}" method="post">
<input type="submit" value="Delete />
</form>
Here's how to do 2 (with jQuery):
$(document).ready(function() {
$("a.delete").click(function() {
var form = $('<form action="/delete/' + this.dataset.id + '" method="post"></form>');
form.submit();
});
});
...
Delete
One thing you should not do is make your delete_entry method respond to GET. GETs are meant to be idempotent (are safe to run repeatedly and don't perform destructive actions). Here's a question with some more details.
Alternatively, change POST to DELETE to get you going.
#app.route('/delete/<postID>', methods=['DELETE'])
Ideally, you should use HTTP DELETE method.
I used flaskr as a base for my Flask project (as it looks like you did as well).
In the .py:
#app.route('/delete', methods=['POST'])
def delete_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('delete from entries where id = ?', [request.form['entry_id']])
g.db.commit()
flash('Entry deleted')
return redirect(url_for('show_entries'))
In the HTML:
<form action="{{ url_for('delete_entry') }}" method=post class=delete-entry>
<input type="hidden" name="entry_id" value="{{ entry.id }}">
<input type="submit" value="Delete" />
</form>
I wanted a button, but you could easily use a link with the solution here.
A simple <a href= link in HTML submits a GET request, but your route allows only PUT requests.
<a> does not support PUT requests.
You have to submit the request with a form and/or with JavaScript code.
(See Make a link use POST instead of GET.)

Pyramid self.request.POST is empty - no post data available

I'm currently working on a pyramid project, however I can't seem to submit POST data to the app from a form.
I've created a basic form such as:
<form method="post" role="form" action="/account/register">
<div class="form-group">
<label for="email">Email address:</label>
<input type="email" class="form-control" id="email" placeholder="you#domain.com">
<p class="help-block">Your email address will be used as your username</p>
</div>
<!-- Other items removed -->
</form>
and I have the following route config defined:
# projects __init__.py file
config.add_route('account', '/account/{action}', request_method='GET')
config.add_route('account_post', '/account/{action}', request_method='POST')
# inside my views file
#view_config(route_name='account', match_param="action=register", request_method='GET', renderer='templates/account/register.jinja2')
def register(self):
return {'project': 'Register'}
#view_config(route_name='account_post', match_param="action=register", request_method='POST', renderer='templates/account/register.jinja2')
def register_POST(self):
return {'project': 'Register_POST'}
Now, using the debugger in PyCharm as well as the debug button in pyramid, I've confirmed that the initial GET request to view the form is being processed by the register method, and when I hit the submit button the POST request is processed by the *register_POST* method.
However, my problem is that debugging from within the *register_POST* method, the self.request.POST dict is empty. Also, when I check the debug button on the page, the POST request is registered in the list, but the POST data is empty.
Am I missing something, or is there some other way of access POST data?
Cheers,
Justin
I've managed to get it working. Silly me, coming from an ASP.NET background forgot the basics of POST form submissions, and that's each form field needs a name== attribute. As soon as I put them in, everything started working.
That does nothing, I belive.
return {'project': 'Register_POST'}
POST parameters are stored inside request, so you have to do something like this.
def register_POST(self, request):
return {'project': request.POST}
To access email input (which has to be named, for example: name="email"), use get() method:
request.POST.get('email')
<form method="post" role="form" action="/account/register"> {% csrf_token %}
Try using "csrf token". hope it works. remaining code looks fine.

Categories

Resources