How can I stop Flask from re-adding form data to database on refresh?
My form.html sends data to approved.html like so:
if request.method == 'POST':
element = db_name(request.form.get('element'))
db.session.add(element)
db.session.commit()
else:
return redirect(url_for('home'))
return render_template('approved.html', current=element)
This so I can display the data from the form and let the user know entry has been added. But the problem is whenever I refresh approved.html which displays the form data, another copy of this entry is added to the database.
This happens because the browser stores the state of the last request and refreshing will re-submit the form, leading to another entry in your database. This is also why it's normal practice to redirect on the server-side after successful form submission. See Preventing POST on reloading a form and here
What you need to do is to successfully handle a successful form request and then redirect the user to a fresh state.
So something like this
return redirect(url_for('success'))
can be added instead of the render_template function which will then be redirected to the assigned function; there you can call the render_template of your choice.
Related
I have created a sign-up page with username and password form inputs that send a POST request upon entering and I wish to send it to my database
But I see that even on clicking the reload button, a POST request is sent.
This is my python-flask code for the route
#app.route('/signup',methods=["GET","POST"])
def signup():
return render_template('signup.htm')
if request.method=="POST":
password = request.form['password']
Is there an option to specify that clicking the reload button SENDS ONLY A GET REQUEST?
First of all in your code, nothing will run after the return statement so the if statement wont run.
Secondly you should separate your functions. By this I mean that you should have a separate function for rendering the page and for handling the form response
Such as:
#app.route('/signup',methods=["GET"])
def signup():
return render_template('signup.htm')
#app.route('/submitsignup',methods=["POST"])
password = request.form['password']
return 'Form submitted'
My form resubmits every time I refresh the page. I want to send context with my render
if request.method == 'POST' and 'search_form' in request.POST:
rol = request.POST['rol']
group = request.POST['group']
national_number = request.POST['national_number']
phone_number = request.POST['phone_number']
users = MyUser.objects.filter(Q(group__name=group)|Q(role__name=rol)|Q(national_number=national_number)|Q(phone_number=phone_number))
return render(request,'deletuser.html',locals())
If you directly render a page as the result of a POST, then refreshing that page will resubmit the form, because that's how you got there.
The usual way to avoid this is to issue an HTTP redirect after a POST, instead of rendering content directly. Then the user ends up on a page via GET, which can be refreshed without problems.
You can google search for "redirect after post" to get more details.
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)
Suppose I have a view like this:
def home(request, redir_url, template = 'example/home.html')
if request.session["profile_name"] and request.session["token"]:
return HttpResponseRedirect(redir_url)
if request.POST:
driver = Facebook()
res = driver.RetLoginUrl(redir_url)
return HttpResponseRedirect(res)
return render(request, template)
In this view, first I check the session variables, if the user has already logged in, I redirect to the welcome page, and if not I have a login button with which the user can authorize my app with Facebook.
And the template has a form method=POST with the login button as input.
My question is, how does the if statement (if request.POST) get executed, when there is a statement return render(request, template) at the end of the view. After the page is rendered, i.e. return render() statement has been executed, shouldn't the view function terminate, hence without a form response being submitted to the view?
Basically, I just would like to understand the execution flow of a django view. Is the return render() statement executed first, and then waits for user input?
It should be
if request.method == 'POST'
This sentence is executed every time you access the url configured for this view. If the method of the request was POST, when the user presses the button, then the code inside if is executed and a HttpResponse is returned
In the example the line render(request, template) is only executed when the method was'nt POST (maybe GET, PUT, DELETE, etc).
Finally, you could use Django Login Decorator to avoid the session variables checking
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.