Flask Input validated before submitted? - python

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">

Related

How to move variables from python to template and back?

I'm trying to get information from a form to my python file and then put into a template. Thing is, i know the form is working but i couldn't show it into the template.
Form here:
<div class="container" id="cont1">
<form action="http://127.0.0.1:5000/areas" method="POST" enctype="multipart/form-data">
<label for="author">Autor</label>
<input type="text" id="author" name="author"><br><br>
<label for="intro">Introdução</label>
<input type="text" id="intro" name="intro"><br><br>
<label for="content">Conteúdo</label>
<input type="text" id="content" name="content"><br><br>
<input type="file" id="planilha" name="planilha" accept=".csv"><br><br>
<input type="submit" value="Enviar">
</form>
</div>
then i try to get the data in app.py:
#app.route('/areas', methods=['GET', 'POST'])
def areas():
if request.method == "POST":
#app.context_processor
def f1():
aut = request.form.get['author']
intr = request.form['intro']
cont = request.form['content']
return dict(a=aut, i=intr, c=cont)
return render_template("areas.html")
else:
return render_template("areas.html")
I know it's working because i tried it out of the route and it showed what the form had. Now when i try into the route:
AssertionError
AssertionError: A setup function was called after the first request was handled. This usually indicates a bug in the application where a module was not imported and decorators or other functionality was called too late.
To fix this make sure to import all your view modules, database models and everything related at a central place before the application starts serving requests.
The decorator was the solution i found to get the data so i could place into templates like this:
<text>{{a}}</text>
The error is caused by the incorrect usage of the decorator. You don't need a decorator to pass variables to templates. render_template also accepts arguments which you can directly pass into your HTML file.
The following should work:
#app.route('/areas', methods=['GET', 'POST'])
def areas():
if request.method == "POST":
aut = request.form['author']
intr = request.form['intro']
cont = request.form['content']
return render_template("areas.html", a=aut, i=intr, c=cont)
else:
return render_template("areas.html")

Passing Variable from HTML to Python/Flask

Let me try this again. I want to enter a variable into my HTML form to submit, so far from reading the link here How to display a variable in HTML I've tried the following code, which is inside of main.html
<form>
Asset Tag:<br>
<input type="text" name="Asset Tag"><br>
<input type="submit" value="Submit">
<form action="{{ asset_tag }}" method="get">
</form>
I then have a python script that goes like this,
from flask import Flask, render_template
app = Flask('server')
#app.route('/py')
def server():
return render_template('main.html')
#API URL
JSS_API = 'https://apiresource.com'
#Pre-Defined username and password
username = 'username'
password = 'password'
#Ask User for the Asset tag
asset_tag = {{ }}
After the asset tag is entered it just searches through a JSON file for match, the rest doesn't matter so much so I didn't include the next piece of the script.
So Flask renders my HTML just fine and I can submit a value but it's not being passed back to the script, which makes sense as I'm doing the opposite of the link I provided, but I just can't not think of how it's done. Any suggestions?
You have a few issues that I've outlined below. Overall though, the frontend is passing the variable to the backend, it's just that the variables are only accessible via the request object from within the route to which the data is passed.
I am not sure why you have a <form> nested within a <form> here, but you'll want to remove the inner one since it's not doing anything.
You want to setup your form to POST the data to your backend when submitted. If you don't specify an action, then it will POST to the same page the same page that you're currently viewing.
<form method="POST">
Asset Tag:<br>
<input type="text" name="tag"><br>
<input type="submit" value="Submit">
</form>
You need to setup your route to accept a POST request so that it can receive data from the form on your page. See here for more information on HTTP methods.
#app.route('/py', methods=['GET', 'POST'])
Inside of your route, you'll want to check whether it was a GET request (and load a normal page) or whether it was a POST (form data was sent so we should use it)
from flask import request
#app.route('/py', methods=['GET', 'POST'])
def server():
if request.method == 'POST':
# Then get the data from the form
tag = request.form['tag']
# Get the username/password associated with this tag
user, password = tag_lookup(tag)
# Generate just a boring response
return 'The credentials for %s are %s and %s' % (tag, user, password)
# Or you could have a custom template for displaying the info
# return render_template('asset_information.html',
# username=user,
# password=password)
# Otherwise this was a normal GET request
else:
return render_template('main.html')

Why does html POST take me to an unexpected page?

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.

400 Bad request from flask when using a page from a sub folder & submitting a form

Whenever I request the page, it loads fine, but when I submit the form, I receive a 400 bad request,I believe it may be to do with the actual html , but I cannot figure out what.
Html file /admin/dashboard.html :
<div class="container">
<p><strong>Shut down server?</strong></p>
<form action="/" method="POST">
<input class="btn btn-danger" id="fred" name="fred" type="submit" value="fred"></input>
</form>
</div>
Python file :
#app.route('/admin/dashboard', methods=['GET', 'POST'])
def admin_dashboard():
if request.method == 'GET':
return render_template("/admin/dashboard.html")
if request.method == 'POST':
if request.form['submit'] == 'fred':
admin.log("Shutting down...")
#os._exit(1)
else:
return render_template("/admin/dashboard.html")
return render_template("/admin/dashboard.html")
You are posting to the / path, but the view that handles your post is at /admin/dashboard. Remove the action="/" line from your form since it's the same url that rendered the page.
If you do need a different url, use url_for('name_of_endpoint'). For example, to generate the url for def admin_dashboard():, you use url_for('admin_dashboard').
Additionally, you named your input 'fred', so you need to access request.form['fred'], or change the name of the input to 'submit'.
You are checking if a field with name submit is present in the request:
if request.form['submit'] == 'fred':
But there is no such field, the field is actually named fred:
<input class="btn btn-danger" id="fred" name="fred" type="submit" value="fred"></input>
You should either change the request check:
if request.form['fred'] == 'fred':
Or change the inputs name:
<input class="btn btn-danger" id="fred" name="submit" type="submit" value="fred"></input>
In Flask doc
We recommend accessing URL parameters with get or by catching the
KeyError because users might change the URL and presenting them a 400
bad request page in that case is not user friendly.
so maybe you can try
request.form.get('submit', '')
so your code:
if request.method == 'POST':
if request.form.get('submit') == 'fred':
admin.log("Shutting down...")
#os._exit(1)
else:
return render_template("/admin/dashboard.html")
return render_template("/admin/dashboard.html")

WTForms double POST submit prevention on refresh flask-wtf

I use WTForms and Flask with Flask-WTF extension
My form looks like:
class CommentForm(Form):
body = TextAreaField('Body', [validators.Length(min=4, max=300)])
entity_id = HiddenField('Entity ID', [validators.required()])
Jinja2 Template:
<form method="POST" action="{{ request.url }}#comment-question" id="comment-question">
<div>{{ comment_form.body }} <button type="submit">Submit</button></div>
{{ comment_form.entity_id(value=question.id) }}
{{ comment_form.hidden_tag() }}
</form>
Rendered form:
<form method="POST" action="http://localhost:5000/answers/1/question-0#comment-question" id="comment-question">
<div><textarea id="body" name="body"></textarea> <button type="submit">Submit</button></div>
<input id="entity_id" name="entity_id" type="hidden" value="1">
<div style="display:none;"><input id="csrf_token" name="csrf_token" type="hidden" value="20120507081937##ee73cc3cfc053266fef78b48cc645cbf90e8fba6"><input id="entity_id" name="entity_id" type="hidden" value=""></div>
</form>
Is it possible to prevent the double form submit on the browser refresh button click without changing the form "action" and doing redirects?
I don't have too much experience using WTForms or Flask, but Django class-based views prevent double-posts by redirecting after a POST, so I had assumed performing a redirect is the way to go for this sort of thing.
One alternative is to generate a unique token and attach it to your form parameters (much like a CSRF token). Cache this value and check against it on form submission. A rather primitive example for Django can be found here.
Edit: Sample code
Although I would really just go with performing a redirect after a successful form submission, here's an example of generating a form token which borrows heavily from this Flask snippet on CSRF protection:
# yourapp/views/filters.py
import random
from string import ascii_letters, digits
from flask import request, session, redirect
from yourapp import app
def generate_form_token():
"""Sets a token to prevent double posts."""
if '_form_token' not in session:
form_token = \
''.join([random.choice(ascii_letters+digits) for i in range(32)])
session['_form_token'] = form_token
return session['_form_token']
#app.before_request
def check_form_token():
"""Checks for a valid form token in POST requests."""
if request.method == 'POST':
token = session.pop('_form_token', None)
if not token or token != request.form.get('_form_token'):
redirect(request.url)
app.jinja_env.globals['form_token'] = generate_form_token
And in your template:
<!-- Again, I've never used WTForms so I'm not sure if this would change when using that app. -->
<input type='hidden' name='_form_token' value='{{ form_token() }}' />
Note that using the CSRF protection method in the snippet also accomplishes pretty much the same effect (although the above code performs a redirect, while the snippet returns a 403).
But this really begs the question--if you're performing a redirect on an invalid token, why not get rid of all this complexity and just redirect on successful form submission?

Categories