Flask form submit both succeeds and errors - python

I have a flask form which works perfect and stores values in my database, but it seems to both succeed (posts values to the database and shows success flash) and fails (shows error and doesn't redirect).
view.py
from flask import render_template, Blueprint, request, redirect, url_for, flash
from project import db
from .models import Items
from .forms import ItemsForm
items_blueprint = Blueprint('items', __name__, template_folder='templates')
#items_blueprint.route('/', methods=['GET', 'POST'])
def all_items():
all_user_items = Items.query.filter_by()
return render_template('all_items.html', items=all_user_items)
#items_blueprint.route('/add', methods=['GET', 'POST'])
def add_item():
form = ItemsForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
try:
new_item = Items(form.name.data, form.notes.data)
db.session.add(new_item)
db.session.commit()
flash('Item added', 'success')
return redirect(url_for('all_items'))
except:
db.session.rollback()
flash('Something went wrong', 'error')
return render_template('add_item.html', form=form)
Output Example
What might be causing this, as I thought it would be one or the other.

I looked into it because of the #NoCommandLine answer. The point is, that the all_items function is located in the blueprint, not in the base of the application. To redirect to it you want to write redirect(url_for(".all_items") (notice the full stop at the first position of the string).See the documentation for url_for, there is an example for a blueprint containing an index function. The full stop makes it search in the same blueprint the current route is in.

It all depends on where the error occurred. Since it flashed - ('Item added', 'success'), it means your error is on the line redirect(url_for('all_items')).
You should look at the code for redirect(url_for('all_items')) and check if there is an issue with all_user_items = Items.query.filter_by(). Maybe that query is faulty. You can also try to print out the error in the except block to see what it is

Related

How to import a file in Flask uWSGI Nginx?

I checked every SO question about it, but the answers are mainly on import errors while I do not have such a problem. Mainly I followed this article followed by this one to have a functioning registration.
Instead of using Flask-SQLalchemy I wanted to create my own database (for fun), but when I try to access the database (DButils.py) functions it occurs an internal server error.
The flask code at the top is:
from flask import Flask, render_template, flash, redirect, url_for, session,
from wtforms import Form, StringField, TextAreaField, PasswordField, validators
from functools import wraps
from DButils import *
My folder follows the same order of the git, with DButils.py in the same folder as app.py.
I did not encounter the error when I import the module, but only when I try to call its functions. In DButils.py I have only a signup function:
def signup(nick, email, password):
return True
And when I try to call it in the app.py code like:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if request.method == 'POST' and form.validate():
email = form.email.data
nick = form.nick.data
password = form.password.data
signup(nick,email,password) #WHEN COMMENTED NO ERROR OCCURS
return redirect(url_for('login'))
return render_template('register.html', form=form)
I get the message "Internal Server Error" with no other clue about it. What can it be? How can I call a function in an external module in Flask?
Thanks for your help!
I found the answer by an trial-error approach. Apparently using pkill --signal SIGHUP uwsgi in combination with sudo systemctl restart nginx.

Flask redirects to wrong view when redirecting to index

I keep running into this strange issue that I can't seem to figure out a solution for. I cannot copy and show all of my code in it's entirety here, but I will try to outline the general structure of my flask app to present my issue.
(Let's ignore all of the content in the /static folder and my helper modules)
I have 3 main views, let's call them viewA, viewB, and index:
viewA.html
viewB.html
index.html
viewA and viewB both display two forms, but with different content (i.e. viewA displays form1 & form2, and viewB also displays form1 & form2).
A simplified version of my script code is as follows:
#imports
from flask import Flask, render_template, session, redirect, url_for, request
from flask_wtf import FlaskForm
#etc. etc.
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blah blah blah'
manager = Manager(app)
bootstrap = Bootstrap(app)
moment = Moment(app)
class FormOne(FlaskForm):
sample_field = StringField('Sample Field:')
class FormTwo(FlaskForm):
other_field = StringField('Other Field:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToA(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
class UploadToB(FlaskForm):
content= StringField('Content to send to view A:', validators=[Required()])
submit = SubmitField('Submit')
#app.route('/ViewA', methods=['GET', 'POST'])
def view_a():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewA.html', form1=form1, form2=form2)
#app.route('/ViewB', methods=['GET', 'POST'])
def view_b():
"""
A lot of data manipulation
"""
form1 = FormOne()
form2 = FormTwo()
if request.method == 'GET':
"""
populate forms with content
"""
if request.method == 'POST':
if form2.validate_on_submit();
"""
clear session variables
"""
return redirect(url_for('index'), code=302)
return render_template('viewB.html', form1=form1, form2=form2)
#app.route('/', methods=['GET', 'POST'])
def index():
"""
Some data manipulation
"""
formA = UploadToA()
formB = UploadToB()
if formA.validate_on_submit()':
"""
pull content from form A
create some session variables
"""
return redirect(url_for('view_a'))
if formB.validate_on_submit()':
"""
pull content from form B
create some session variables
"""
return redirect(url_for('view_b'))
return render_template('index.html', formA=formA, formB=formB)
if __name__ == '__main__':
manager.run()
Now the issue at hand I am having here is that for some strange reason when I'm in 'viewA.html' and I submit my form, I SHOULD be redirected back to 'index.html' but for some strange reason it redirects me to 'viewB.html'. Furthermore, the opposite also holds true: when i'm in 'viewB.html' and I submit my form, I SHOULD also be redirected back to 'index.html' but it redirects me to 'viewA.html'. Yet, if I am in either viewA or viewB, I have no issues of going back to the index view if I manually enter the url into my browser.
Any ideas as to why I might be running into this issue?
Thanks in advance :)
I have finally figured out the source of my problem. It turns out that in my 'viewA.html' template file, I had the following in my < form > tag:
<form class="form form-horizontal" method="post" role="form" action="{{url_for('index')}}">
And the problem all lies in that last part:
action="{{url_for('index')}}"
As a result, everytime I would submit form2 in viewA.html it would create a post request for my index page rather than a post request for the viewA.html page (which caused a redirect to the wrong view). Thus, by simply removing the action attribute (action="{{url_for('index')}}"), I was able to solve my problem!
Since the full code isn't here, I can't confirm this for sure, but what I think is happening is this:
You open form A
You submit form A
It sends a redirect to /index
It sends a redirect to /FormB
if formB.validate_on_submit():
return redirect(url_for('view_b'))
This is probably sending a redirect to View B. Try changing that last line to something like return something_else and seeing if it sends that after submitting form A.

Flask-Cache doesn't distinguish between GET and POST

I have a Flask route structured like so:
#app.route('/rootpath1/<path:path>')
#app.route('/rootpath2/<path:path>', methods=['GET', 'POST'])
#cache.cached()
def rootpath():
...
POSTs to '/rootpath2/' for a given page are typically retrieved from cache (when a cached value is present), which is usually the last GET request.
For example, a user would visit '/rootpath2/myform', fill out and then submit the form. The form would post to '/rootpath2/myform' and the user would be returned to the same URI with a message indicating that the form submission was successful (or that errors occurred, if they did).
The problem here is that a GET always precedes the POST, and the POST always triggers a cache hit and returns that value.
Is there a way for Flask-Cache to distinguish between GETs and POSTs and handle them according (only caching GETs)?
Yes. The cache decorator provides an unless kwarg that accepts a callable. Return True from the callable to cancel caching. Test it out with the following:
from flask import Flask, request, render_template_string
from flask.ext.cache import Cache
app = Flask('foo')
cache = Cache(app,config={'CACHE_TYPE': 'simple'})
t = '<form action="/" method="POST">{{request.form.dob}}<input name="dob" type="date" value={{request.form.dob}} /><button>Go</button></form>'
def only_cache_get(*args, **kwargs):
if request.method == 'GET':
return False
return True
#app.route('/', methods=['GET', 'POST'])
#cache.cached(timeout=100, unless=only_cache_get)
def home():
if request.method == 'GET':
print('GET is not cached')
return render_template_string(t)
if request.method == 'POST':
print('POST is not cached')
return render_template_string(t)
app.run(debug=True)

Redirect to new page with path and optional argument in Flask and Flask-WTF not working

This question already has answers here:
Flask view return error "View function did not return a response"
(3 answers)
Closed 2 days ago.
I am using Flask and Flask-WTF and I have the following code in my views.py file:
from flask import render_template, flash, redirect, url_for
from . import app, forms
#app.route('/', methods=['GET', 'POST'])
#app.route('/index', methods=['GET', 'POST'])
def index():
form = forms.HelpSearch()
if form.validate_on_submit():
flash('Searched for: %s' % form.value.data)
redirect(url_for('help', form.value.data))
return render_template('index.html', title='Index', form=form)
#app.route('/help/<keyword>', methods=['GET', 'POST'])
def help(keyword=None):
form = forms.HelpSearch()
if form.validate_on_submit():
flash('Searched for: %s' % form.value.data)
redirect(url_for('help', keyword=form.value.data))
# This is just some dummy data for testing my template
keywords = ['n', 'north']
groups = ['movement']
syntax = [
{'cmd':"n", 'args': ''},
{'cmd':'north', 'args': ''}
]
content = 'Move north'
return render_template('show_help.html',
title=keyword,
form=form,
keywords=keywords,
groups=groups,
syntax=syntax,
content=content)
What I want, and expect, it to do is that when someone puts some text in the forms search field and hits the search button it returns that value and then I redirect to the appropriate page, e.g. they search for foo and end up at /help/foo.
Sadly the redirect from the form validation bit is not redirecting as desired. It just appears to be reloading the current page.
I know for a fact that the form is getting and returning the data because the flash call is showing up with the correct info, e.g. 'Searched for: foo' but when I pass the keyword to url_for the page, again, simply reloads. Manually navigating to /help/foo works fine.
I have tested that url_for is working and it creates the appropriate path as desired when I enter a keyword manually, e.g. print url_for('help', keyword='foo') prints /help/foo.
Anyone have any idea why it is not redirecting as desired?
Edit: Got it running on Heroku if anyone wants to see what exactly is happening.
I think your problem is for not returning anything
you can check this:
def index():
form = forms.HelpSearch()
if form.validate_on_submit():
flash('Searched for: %s' % form.value.data)
return redirect(url_for('help', keyword=form.value.data))
return render_template('index.html', title='Index', form=form)
The problem is in how you are doing your redirects. Instead of this:
redirect(url_for('help', keyword=form.value.data))
do this:
return redirect(url_for('help', keyword=form.value.data))
The redirect() function does not raise an exception like abort() does, it just returns a Response object that needs to be passed up the stack.

Flask login_required + next url params

I have a protected view in my app which just accepts POST requests.
#app.route("/booking", methods=("POST", ))
#login_required
def booking():
arg1 = request.form.get("arg1")
arg2 = request.form.get("arg2")
When an unauthorized user tries to access this view, I want them to
login and then be redirected here.
Right now, my login view looks like this:
#app.route("/login", methods=("GET", "POST"))
#login_required
def login():
do_login()
return redirect(request.args.get('next') or url_for('home'))
So what ends up happening is a POST request to /booking (which is the
"next" parameter) and I get a NOT ALLOWED error.
The problem is that login() makes a GET request to booking(). I can
get around that, but I am not sure how to retrieve the original POST
form arguments from /booking? Any ideas to get round that?
I would solve this by pulling the data and putting it in the session. You can remove the #login_required decorator and check this in the function using current_user.is_authorized. See Flask Sessions and Flask Login.
Something like this might work for you, I didn't test it:
from flask import session
from flask_login import current_user
#app.route("/booking", methods=("POST", ))
def booking():
if not 'arg1' in session.keys() and not 'arg2' in session.keys():
session['arg1'] = request.form.get("arg1")
session['arg2'] = request.form.get("arg2")
# Now the data will persist in the session
if current_user.is_authorized:
# Do what you need...
else:
# Redirect to login, session will persist
Why would you only use POST in the booking view ? You are probably rendering a form which should also allow GET.
#app.route("/booking", methods=['GET','POST'])
#login_required
def booking():
# render the form. something like
form = BookingForm()
# Check if POST
if request.method == 'POST':
# process the form now and do whatever you need.
return redirect(url_for('index'))
# code below will run if not POST. You should render the template here
return render_templte('booking.html')

Categories

Resources