I'm trying to submit form data to a route, similar to how a browser would, but am having trouble. I've simplified the code provided to the minimal I need to reproduce the problem. I'm probably not using a function I should or doing something else wrong but I can't find the answer in all the other posts I've searched. I don't want the data to be on the URL but rather accessible via request.form. I am using Python 2.7.9. I know the code shown below currently does NOT do what I want (it puts the formdata dictionary on the command line) and returns an endless loop because the formdata is never read. I understand this, I just did this to show that I'm trying to pass the formdata to the post function, I just don't know how to do it and also use redirect at the same time. If I'm reading the redirect documentation correctly it seems to say you can't do this. So is there some other function I can use that will pass the parameters to a URL and also provide the form data? I'm not using the parameters in the code below but my actual code has a similar function structure where I do use them. The main goal here is to have the same function (URL path) used when "choice" is or is not submitted via a form. Any help is appreciated. Thanks.
from flask import Flask, redirect, url_for, request, render_template
web_server = Flask(__name__)
#web_server.route("/test_<param1>_<param2>",methods=['POST','GET'])
def test1(param1,param2):
if request.method == 'GET':
choice1 = 'No choice made yet'
templateData = {'choice':choice1}
return render_template('form_page.html', **templateData)
elif request.method == 'POST':
if 'choice' in request.form:
choice1 = request.form['choice']
templateData = {'choice':choice1}
return render_template('form_page.html', **templateData)
else:
formdata = {'choice':'QQQ'}
p1='AAA'
p2='BBB'
# the following line is NOT what I want to do. See question
return redirect(url_for('.test1',param1=p1,param2=p2,data=formdata),code=307)
# run server
if __name__ == "__main__":
web_server.run(host='0.0.0.0',port=80,debug=True)
HTML:
Choice was {{choice}}<br><br>
<form method="post">
Make a choice:
<input type="text" name="choice"><br><br>
<input type="submit">
</form>
<form method="post">
Do anything else:
<input type="text" name="other"><br><br>
<input type="submit">
</form>
lets see if this is what you mean, see quick and dirty code below. Have a close look at the request.args statement, that is where you can get the variables stored in the url.
from flask import Flask, redirect, url_for, request, render_template
app = Flask(__name__)
#app.route('/',methods=['POST','GET'])
def test1():
if request.method == 'POST':
if 'choice' in request.form:
choice = request.form['choice']
return render_template('test.html', choice = choice)
else:
choice = 'QQQ'
p1 = 'AAA'
p2 = 'BBB'
return redirect(url_for('.test1',param1 = p1, param2 = p2, choice = choice))
choice = request.args.get("choice")
if choice == None:
choice = 'No choice made yet'
return render_template('test.html', choice = choice)
if __name__ == "__main__":
app.run(host='0.0.0.0',port=5000,debug=True)
Related
I am learning Flask. I wrote the basic code and I want the submitted text to display in the same page. I already wrote the html and connected it. How can I do this?
from flask import Flask, redirect, url_for,render_template, request
app = Flask(name)
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
user = request.form["nm"]
return redirect(url_for("/", user))
else:
return render_template("login.html")
if name == ("main"):
app.run(debug=True)
I've noticed that you've taken the code from Python Basics. Indeed they do not show how to format the HTML template of the redirect.
Luckily, they offer a tutorial that shows you how to feed retrieved data to an HTML template using Jinja2. This tutorial can be found here. In essence, you can use {{ variable }} in your HTML template. In Flask, you will have to specify the variable as argument in the render_template function.
Minimal example:
# app.py
#app.route('/result',methods = ['POST', 'GET'])
def result():
if request.method == 'POST':
variable = request.form['variable']
return render_template("result.html", variable=variable)
<!-- result.html -->
<p> This is your variable: {{ variable }} </p>
I advice you to also check out both the Flask and Jinja2 documentation, as they offer plenty comprehensive examples of how to work with callbacks and HTML templating.
I am trying to get users amount of coins saved to my flask project. So users are supposed to write in a input field how much coins they want to display, for instance "1200", then I want flask to receive that and print it on my table I have. I have done some research, and also tried copy a bit of code from my "contact" form, but no luck at all!
Here is my HTML code:
div class="search_div">
<form action="GET">
<input type="text" class="coins-box" placeholder="Amount of Coins" />
</form>
</div>
Here is my python code:
#app.route('/bflipper', methods=['GET', 'POST'])
def bFlipper():
if request.method == 'GET':
userInput = request.form['input']
return render_template('flipper.html', userInput=userInput)
I mainly want it to be stored into a variable on python, as I need to add some other stuff with that user input later on!
Make a global variable and assign value to it inside the function
my_value = ""
#app.route('/bflipper', methods=['GET', 'POST'])
def bFlipper():
if request.method == 'GET':
global my_value
my_value = request.form['input']
I am a newbie to flask and trying to make form submission with two webpages.
Basically, there are two webpages, in the app.py they are routed as
#app.route('/')
def index():
...
#app.route('/results', methods=['POST'])
def results():
...
There are one form submission on '/' and one on '/results'. Right now, clicking the button on '/' redirects the user to '/results', with the input text sent to '/results' as well
<form method=post action='/results'>
<dl>
{{ render_field(form.channel_title ) }}
</dl>
<input type=submit value='analyze' name='sumbit_btn'>
</form>
This part works. What I want to do now is to click the button on '/results' such that the text in its input form is used to update some text on itself. The problem is that in order to render '/results', input from '/' is needed.
def results():
....
return render_template('results.html', channel=channel)
How can I implement this form submission on '/results' then, which send both its own form input and also the old input from '/' such that '/results' can be updated? Any suggestion is appreciated. Thanks.
You can add the data to the flask session.
from flask import Flask, session, abort, request, render_template
#app.route('/results', methods=['POST'])
def results():
if request.form['channelname'] in ALLOWED_CHANNELS:
session['channel'] = request.form['channelname']
if 'channel' in session:
return render_template('results.html', channel=session['channel'])
else:
abort(400)
I'm writing a webapplication using bottle.py, Beaker for sessions and a custom AAA written module, as many I'm worried about security and the best method to protect against some targeted attack like the one I've mentioned.
As an example I have the following code:
#route('/manage/adddomain',method='POST')
def adddomain():
#This checks if user has enough power to create a domain
aaa.require(50,'/forbidden')
user = aaa.getusername() # This is retrieved from a server side session
domainname = request.forms.get('domain')
description = request.forms.get('description')
# Additional checks are performed in the sql module
# to protect against forged requests with valid login
return sql.createdomain(user,domainname,description)
What additional checks would you perform to secure your webapplication?
Blender pretty much covered what you need, but I would like to add another method. Instead of checking on each POST, you can add a wrapper like:
def wrap_requires_csrf(*methods):
def wrapper(fn):
#wraps(fn)
def wrapped(*args, **kwargs):
if request.method in methods:
if request.method == 'POST':
csrf = request.form.get('csrf')
elif request.method == 'GET':
csrf = request.args.get('csrf')
if not csrf or csrf != session.get('csrf'):
abort(400)
session['csrf'] = generate_csrf_token()
return fn(*args, **kwargs)
return wrapped
return wrapper
#app.route('/some/page', methods=['GET','POST'])
#wrap_requires_csrf('POST')
def some_page():
...
Then, in your template, you would provide the hidden field with
<input name="csrf" type="hidden" name="{{session.csrf}}" />
You need to include a CSRF token in every important form field and sanitize all output with your template engine.
Here's a Flask snippet that you can adapt to your Bottle app:
A common technique against CSRF attacks is to add a random string
to the session, and check that string against a hidden field in the
POST.
#app.before_request
def csrf_protect():
if request.method == "POST":
token = session.pop('_csrf_token', None)
if not token or token != request.form.get('_csrf_token'):
abort(403)
def generate_csrf_token():
if '_csrf_token' not in session:
session['_csrf_token'] = some_random_string()
return session['_csrf_token']
app.jinja_env.globals['csrf_token'] = generate_csrf_token
And then in your template:
<form method=post action="">
<input name=_csrf_token type=hidden value="{{ csrf_token() }}">
As for sanitizing, that's dependent upon your template engine. Jinja2 has the e or escape filter:
<h2>No results for {{ search_query|escape }}</h2>
I adapted #Blender's solution to bottle, and here it is:
from string import ascii_letters, digits
from random import choice
from bottle import Bottle, request, Jinja2Template, abort
app = Bottle()
#app.hook('before_request')
def csrf_protect():
if request.method == 'POST':
sess = request.environ['beaker.session']
req_token = request.forms.get('csrf_token')
# if no token is in session or it doesn't match the request one, abort
if 'csrf_token' not in sess or sess['csrf_token'] != req_token:
abort(403)
def str_random(length):
'''Generate a random string using range [a-zA-Z0-9].'''
chars = ascii_letters + digits
return ''.join([choice(chars) for i in range(length)])
def gen_token():
'''Put a generated token in session if none exist and return it.'''
sess = request.environ['beaker.session']
if 'csrf_token' not in sess:
sess['csrf_token'] = str_random(32)
return sess['csrf_token']
# allow access of the token generator using csrf_token
Jinja2Template.defaults['csrf_token'] = gen_token
Bottle does not come with sessions by default, so I'm using bottle-beaker.
The input which needs to be included in every form:
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
I'm trying to use flask.g to store variables that can be accessed in other functions, but I don't seem to be doing something correctly. The application generates the following error when I try to access g.name: AttributeError: '_RequestGlobals' object has no attribute 'name'.
The documentation for flask.g says:
Just store on this whatever you want. For example a database
connection or the user that is currently logged in.
Here's a complete, minimal example that illustrates the error that I receive when trying to access the variable outside of the function it was created in. Any help would be greatly appreciated.
#!/usr/bin/env python
from flask import Flask, render_template_string, request, redirect, url_for, g
from wtforms import Form, TextField
application = app = Flask('wsgi')
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
g.name = name
# Need to create an instance of a class and access that in another route
#g.api = CustomApi(name)
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
# Need to access the instance of CustomApi here
#api = g.api
name = g.name
return render_template_string(name_template, name=name)
class LoginForm(Form):
name = TextField('Name')
template_form = """
{% block content %}
<h1>Enter your name</h1>
<form method="POST" action="/">
<div>{{ form.name.label }} {{ form.name() }}</div><br>
<button type="submit" class="btn">Submit</button>
</form>
{% endblock %}
"""
name_template = """
{% block content %}
<div>"Hello {{ name }}"</div><br>
{% endblock %}
"""
if __name__ == '__main__':
app.run(debug=True)
The g object is a request-based object and does not persist between requests, i.e. g is recreated between your request to index and your request to get_posts.
Application Globals in Flask:
Flask provides you with a special object that ensures it is only valid for the active request and that will return different values for each request. In a nutshell: it does the right thing, like it does for request and session.
For persistent storage of tiny data between requests use sessions instead. You may (but should not) get away with storing the data in the app object directly for global (all sessions) application state, similar to what config does, if you find a really good reason to do so.
For more complex data use databases.
If you need to track authentication information, I'd suggest one of the Flask plugins like Flask-Login or Flask-Principal.
For example, we use Flask-Principal. It raises the identity-loaded signal when somebody authenticates (or it detects an authentication cookie). We then map their logged-in identity with a user in our database. Something like this:
# not actual code
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
user = Person.query.filter(Person.username==identity.person.username).one()
g.user = user
and then we can use g.user in any controller or template. (We're actually ripping a lot of this out, it was a easy, lazy hack that's caused more trouble than it's worth.)
If you don't want to use a module, there's a built-in signal you can hook into at the start of every request:
http://flask.pocoo.org/docs/tutorial/dbcon/
# This runs before every request
#app.before_request
def before_request():
g.user = your_magic_user_function()
and g.user would then be magically available everywhere.
I hope that helps!
Just use sessions in flask. In your case, you just want to save the user/name in your request and the easiest way is to use sessions.
from flask import session
app.secret_key = 'some key for session'
Then, your functions could be changed as below:
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
session['name'] = form.name.data
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
if 'name' in session:
name = session['name']
else:
name = "Unknown"
return render_template_string(name_template, name=name)
I will like to shed more light on the use of g global in storing data. g only store data with a request and when redirecting to another route, the g global is set back to null i.e it reset back to nothing. This means whatever set to g in one request can't be access in another request. Use sessions to store data that will be accessed across request.
One benefit of using g global is when connecting to a database to fetct a user. For example, may be the admin from the database. The admin can be store in the g global using the below method.
from flask import Flask, g
app = Flask(__name__)
#app.before_request
def text():
g.a = User.query.filter_by(email='admin#gmail.com')
#app.route("/getTrue", methods=['GET', 'POST'])
def getTrue():
form = UserForm()
if form.validate_on_submit():
if g.a == form.email.data:
return "Admin is logged in"
else:
return "User is logged in"
return render_template('login.html', form=form)
In the example above, the g can be use to save data which will be use in another request. I hope this help. Thanks