I am having an issue with my fading modal routing in flask. My user login opens a modal and i m trying to implement the POST feature from the modal in flask.
I thought of implementing under index and search for the form name form the the post like below.
#app.route('/')
#app.route('/index')
def index():
if request.form.get('login', None) == 'submit' :
return 'Yeah hooo'
return render_template('index.html')
However, when i execute the code above, i get
Method Not Allowed
on /index. My other worries is that my Login form is in the template and can therefore be call from any routes. Since template is not a route. Please is there any way i can achieve this in flask ? Or do i have to take the login in to a seperate html file instead of the template ?
You should explicitly add POST to the list of methods that can be processed
#app.route('/', methods=['GET', 'POST'])
See here for more information
As per the second question, it's ok as long as your login form makes POST request to the same route ('/' in your case).
Related
I have noticed that after a POST, if you look at the page source via "control + U" flask opens the source page also again via POST and does all tasks again. Is there any way I can prevent this?
#app.route("/", methods=["POST", "GET"])
def event_page():
if request.method == "POST":
do_something()
when I view the source page the do_something() function is trigger t again.
Not really a Flask problem. The simplest solutions would be not to use CTRL+U but F12 - inspector or network tab
I'm in Flask. I have two forms on one html page, both with post methods to the same route. I have read that it is possible to use control flow to identify which form is being posted, under the same app route. I seem to be battling with the logic here. So far I have the below:
#app.route('/page-with-2-forms, methods=['GET', 'POST'])
def function():
if request.method == "POST":
if 'form_1_element_name' in request.form:
#form1
#request args from this form and do some stuff
#form2
else:
#request args from this form and do some stuff
return render_template('page.html')
The problem here is that when attempting to post #form1 it still attempts to execute the block for #form2 (the else statement). However, when posting #form2 everything works fine. As I said I seem to be missing some logic here.
The element name tag is posted under the html tag. <form id="form_1_element_name" name="form_1_element_name" action="page-with-2-forms" method="POST">
Please note that I know the conventional practice is to use WTForms, but I do not want to use it here
Any help would be massively appreciated
Will post the solution just for future reference and people having similar issues. In the original post, the if statement references the form name, as stated in the html snippet
<form id="form_1_element_name" name="form_1_element_name" action="page-with-2-forms" method="POST">
This doesn't work. Instead, the if statement should reference an element inside the actual form. In the below, I referenced the submit button, which is ID and named to a unique name, such as "editFormSubmitButton".
#app.route('/page-with-2-forms, methods=['GET', 'POST'])
def function():
if request.method == "POST":
if 'editFormSubmitButton' in request.form:
#form1
#request args from this form and do some stuff
#form2
else:
#request args from this form and do some stuff
return render_template('page.html')
This is a great alternative to WTForms
Thanks to #go2nirvana for the bit of help
I have a simple flask app where I take an image file path from an input page and then switches over to an output page with the image being displayed. My app works properly and manages to access my saved files and displays them on the new page, but I realized I have two routes that point towards the same place. My code is shown below:
#app.route('/')
def main_page():
return render_template('input_receiver.html')
#app.route('/', methods = ['POST', 'GET'])
def get_input():
if request.method == 'POST':
user = request.form['nm']
return redirect(url_for('success', name=user))
These two functions point towards my initial localhost:5000 page, the first function renders an html file with a text field input and a button. In order to connect to the actual flask script, it includes the following line in the form tag:
<form action = "http://localhost:5000" method = "post">
('nm' is the text field input on the initial HTML page)
The second function takes in the user input of the local image file path and redirects them to an HTML file after they press a button which displays the picture they entered. This is done through the below function.
#app.route('/<name>')
def success(name):
return render_template('popup.html', picture_path = "/static/" + name)
Both routes point towards the initial localhost:5000 page, so how does flask handle execution? If this is considered as a bad way to create this sort of functionality, what is the better way of doing it?
You can put both GET and POST in the same method under same route.
#app.route('/', methods = ['POST', 'GET'])
def get_input():
if request.method == 'POST':
user = request.form['nm']
return redirect(url_for('success', name=user))
return render_template('input_receiver.html')
You have already given the methods as GET and POST for the route / - method get_input(), so you can completely remove the other method - main_page.
Both your GET and POST request can be handled by this one method.
I have a small log browser. It retrieves and displays a list of previously logged records depending on user's input. It does not update anything.
The code is very simple and is working fine. This is a simplified version:
#app.route('/log', methods=['GET', 'POST'])
def log():
form = LogForm()
if form.validate_on_submit():
args = parse(form)
return render_template('log.html', form=form, log=getlog(*args))
return render_template('log.html', form=form)
However it does not follow the post/redirect/get pattern and I want to fix this.
Where should I store the posted data (i.e. the args) between post and get? What is the standard or recommended approach? Should I set a cookie? Should I use flask.session object, create a cache there? Could you please point me in the right direction? Most of the time I'm writing backends...
UPDATE:
I'm posting the resulting code.
#app.route('/log', methods=['POST'])
def log_post():
form = LogForm()
if form.validate_on_submit():
session['logformdata'] = form.data
return redirect(url_for('log'))
# either flash errors here or display them in the template
return render_template('log.html', form=form)
#app.route('/log', methods=['GET'])
def log():
try:
formdata = session.pop('logformdata')
except KeyError:
return render_template('log.html', form=LogForm())
args = parse(formdata)
log = getlog(args)
return render_template('log.html', form=LogForm(data=formdata), log=log)
So, ultimately the post/redirect/get pattern protects against submitting form data more than once. Since your POST here is not actually making any database changes the approach you're using seems fine. Typically in the pattern the POST makes a change to underlying data structure (e.g. UPDATE/INSERT/DELETE), then on the redirect you query the updated data (SELECT) so typically you don't need to "store" anything in between the redirect and get.
With all the being said my approach for this would be to use the Flask session object, which is a cookie that Flask manages for you. You could do something like this:
#app.route('/log', methods=['GET', 'POST'])
def log():
form = LogForm()
if form.validate_on_submit():
args = parse(form)
session['log'] = getlog(*args)
return redirect(url_for('log'))
saved = session.pop('log', None)
return render_template('log.html', form=form, log=saved)
Also, to use session, you must have a secret_key set as part of you application configuration.
Flask Session API
UPDATE 1/9/16
Per ThiefMaster's comment, re-arranged the order of logic here to allow use of WTForms validation methods for invalid form submissions so invalid form submissions are not lost.
The common way to do P/R/G in Flask is this:
#app.route('/log', methods=('GET', 'POST'))
def log():
form = LogForm()
if form.validate_on_submit():
# process the form data
# you can flash() a message here or add something to the session
return redirect(url_for('log'))
# this code is reached when the form was not submitted or failed to validate
# if you add something to the session in case of successful submission, try
# popping it here and pass it to the template
return render_template('log.html', form=form)
By staying on the POSTed page in case the form failed to validate WTForms prefills the fields with the data entered by the user and you can show the errors of each field during form rendering (usually people write some Jinja macros to render a WTForm easily)
I'm trying to have my form submit to a route which will validate the data then redirect back to the original route.
For example:
User loads the page website.com/post
Form POSTs the data to website.com/post-save
User gets redirected back to website.com/post
Pyramid is giving me some troubles doing this.
Here's my slimmed down views.py
def _get_link_form(post_data):
""" Returns the initialised form object """
return LinkForm(post_data)
def home_page(request):
form = _get_link_form(request.POST)
return {'form' : form}
def save_post(request):
""" form data is submitted here """"
form = _get_link_form(request.POST)
if not form.validate():
return home_page(request, form)
This is the code I've been playing around with. Not only does it not work, it also feels messy and hacked up. Surely there's a simpler way to 'redirect after-POST' in Pyramid?
Your problem is most easily solved by simply POSTing to the same URL that your form is shown at, and simply redirecting the user away from the page when the POST is successful. That way until the form is successfully submitted you do not change URLs.
If you're just dying to POST to a different URL, then you need to save the data using sessions, since you're obviously handling the form data between requests.
Typically if you want to be able to handle errors in your forms you would use a session and flash messages. To do this you simply add a location for flash messages to appear in your base template and setup session support using something like pyramid_beaker.
Assuming your home page is setup at the 'home' named-route:
from pyramid.httpexceptions import HTTPFound
def myview(request):
user = '<default user field value>'
if 'submit' in request.POST:
user = request.POST.get('user')
# validate your form data
if <form validates successfully>:
request.session.flash('Form was submitted successfully.')
url = request.route_url('home')
return HTTPFound(location=url)
return {
# globals for rendering your form
'user': user,
}
Notice how if the form fails to validate you use the same code you did to render the form originally, and only if it is successful do you redirect. This format can also handle populating the form with the values used in the submission, and default values.
You can loop through the flash messages in your template of choice using request.session.peek_flash() and request.session.pop_flash().
route_url supports mutating the query string on the generated url as well, if you want to flag your home page view to check the session data.
You can obviously just pass everything in the query string back to the home page, but that's a pretty big security vulnerability that sessions can help protect against.
The Pyramid documentation has a particularly on-point section with the following example:
from pyramid.httpexceptions import HTTPFound
def myview(request):
return HTTPFound(location='http://example.com')
I do this like so:
from pyramid.httpexceptions import HTTPCreated
response = HTTPCreated()
response.location = self.request.resource_url( newResource )
return response
This sends the HTTP Created code , 201
The Pyramid documentation has content about Redirect, you can see more information in below link :
Pyramid documentation
import pyramid.httpexceptions as exc
raise exc.HTTPFound(request.route_url("section1")) # Redirect
Edited:
Actually you can do that on client side with Javascript, first you should send particular response to client side(either with flashes some data or return Response object):
window.location = '{{ request.route_path("route_name") }}';
Assuming your homepage is the default view of your pyramid web app, you can do:
def _get_link_form(post_data):
""" Returns the initialised form object """
return LinkForm(post_data)
def home_page(request):
form = _get_link_form(request.POST)
return {'form' : form}
def save_post(request):
form = _get_link_form(request.POST)
if not form.validate():
from pyramid.httpexceptions import HTTPFound
return HTTPFound(location=request.application_url)
Basically you need to know how the home_page view was "added" to your Configurator. If your homepage actually lives at /few/levels/deep/homepage then a redirect might look like this:
return HTTPFound(location=request.application_url + '/few/levels/deep/homepage')
A clean way is using the "overload" provided by pyramid for different request types, por example, you can decorate your methods this way:
#action(request_method='GET',
renderer='mypackage:/templates/save.mako',
name='save')
def save(request):
''' Fill the template with default values or leave it blank'''
return {}
#action(request_method='POST',
renderer='mypackage:/templates/save.mako',
name='save')
def save_post(request):
""" form data is submitted here """"
# process form
In the HTML, you must call the action form, like
<form method="POST" id="tform" action="${request.route_url('home', action='save')}">
This way, one method is processed when the method POST is used, and the other when the GET is used. The same name, but two implementations.