Flask view shows 400 error instead of template with form - python

I'm trying to display a page with a form, then add a Player to the database when the form is submitted. However, I can't view the form because the browser always shows a 400 Bad Request error. Other posts indicate that this could be because the name of the form input doesn't match the key I get from request.form, but all my keys match. Why do I get this error?
<form method="post">
{{ form.hidden_tag() }}
<input name="name">
<input name="available">
<input type="submit">
</form>
#app.route('/addplayer', methods=['GET', 'POST'])
def addplayer():
connect('basketball_contracts', host='localhost', port=27017)
n = request.form['name']
a = request.form['available']
post= Post(
name=n,
available=a
)
post.tags = ['test']
post.save()
return render_template('addplayer.html', form=form)

Your view accepts GET and POST requests. request.form is only filled out on POST. If you try to access a key that doesn't exist, it raises a 400 error. No keys will exist when you GET the page initially.
The common pattern for this is to guard code that requires request.form in an if request.method == 'POST' block. Return a redirect after handling the POST request, otherwise return the rendered template.
from flask import url_for, redirect, render_template
#app.route('/addplayer', methods=['GET', 'POST'])
def addplayer():
if request.method == 'POST':
Post(
name=request.form['name'],
available=request.form['available']
).save()
return redirect(url_for('index'))
return render_template('addplayer.html')
Since you appear to be using Flask-WTF, you can use the form's validate_on_submit method instead of checking method. In that case, you can also access the data through the form instance, and use the form to render the inputs for you.
from flask import url_for, redirect, render_template
#app.route('/addplayer', methods=['GET', 'POST'])
def addplayer():
form = AddPlayerForm()
if form.validate_on_submit():
Post(
name=form.name.data,
available=form.available.data
).save()
return redirect(url_for('index'))
return render_template('addplayer.html', form=form)
<form method=post>
{{ form.hidden_tag() }}
{{ form.name.label}} {{ form.name }}<br>
{{ form.available.label }} {{ form.available }}<br>
<input type=submit value="Add Player">
</form>

Related

Why my login form in python is not working properly?

I have a problem with a Login Form. I used Flask and Bcrypt.
When i try to login, with a user and password, the login page is not returning anything, i stay in the same login page, the only difference is the url returns this:
http://127.0.0.1:5000/?csrf_token=IjgwZTRhZmI4NWM2MTExNTdiM2I1NmM2YTYzZjIxZTdhMmI4M2JkZTgi.YYwLuQ.bYG6VKjMZ65p4i86S9-U23L9c_A&username=alexcar&password=1234Ab&submit=Login
This is the login form def:
#app.route('/', methods=['GET','POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user:
if bcrypt.check_password_hash(user.password, form.password.data):
login_user(user)
return redirect(url_for('admin'))
return render_template('login.html', form=form)
When the user is authenticated returns the page admin, in Def admin is:
#app.route('/admin', methods=['GET','POST'])
#login_required
def admin():
return render_template('admin.html')
On Login.html form is:
<div class="form-floating">
<form action="" method="post">
{{ form.hidden_tag() }}
{{ form.username }}
<br> </br>
{{ form.password }}
{{ form.submit }}
</form>
</div>
The register form is working well but the login page not.
The problem lies in your return statement after signing in.
Note: The url_for takes in the name of the route function (def admin) not (def .admin)
return redirect(url_for('.admin'))
should be:
return redirect(url_for('admin'))

Django checkout not accessible: Page not found (404)

I'm trying to develop an e-commerce site with Django. So I'm at this point where, users can add items to their cart, but when I try to proceed to checkout, for some reason, my checkout form is not displayed rather, it says:
Page not found (404)
I made sure that I have registered my models, and ran migrations.
What is the problem?
My views.py:
#login_required
def checkout(request):
address_form = UserAddressForm(request.POST or None)
if address_form.is_valid():
new_address = address_form.save(commit= False)
new_address.user = request.user
new_address.save()
else:
raise Http404
print(form.errors)
context = {"address_form": address_form}
template = "orders/checkout.html"
return render(request, template, context)
My checkout.html:
<form method="POST" action=''>
{% csrf_token %}
<fieldset class="form-group">
{{ address_form|crispy }}
</fieldset>
<div class="form-group">
<input type="submit" class="btn btn-outline-dark" value="Place Order"/>
</div>
</form>
My urls.py:
from orders import views as orders_views
path('checkout/', orders_views.checkout, name='checkout'),
You've implemented GET request handling incorrectly, for reference see this example from the docs. In your case form was always invalid because in case of GET request it was initialized with none. However you don't even have to validate empty form on GET request.
Your code updated:
#login_required
def checkout(request):
if request.method == 'POST':
address_form = UserAddressForm(request.POST)
if address_form.is_valid():
new_address = address_form.save(commit= False)
new_address.user = request.user
new_address.save()
return # TODO : return what?
else:
# otherwise (if GET request) we get here
address_form = UserAddressForm()
context = {"address_form": address_form}
return render(request, "orders/checkout.html", context)
And you need to specify what is supposed to happen when the form is valid: redirect for example.

How to redirect while keeping form data using Flask and WTForms?

Let's have a page with a registration form on it. It's in section #registration. If user submits invalid data, the page should return him back to the #registration section and display to which fields were submitted invalid values.
I tried to render template and make response and redirect to it but I'm getting a TypeError:
File ".../app/routes.py", line 28, in index
return redirect(url_for('.index', form=form, _anchor='registration'), 302, response)
File ".../python3.7/site-packages/werkzeug/utils.py", line 507, in redirect
mimetype="text/html",
TypeError: __call__() got an unexpected keyword argument 'mimetype'
The function looks like this:
#app.route('/', methods=['GET', 'POST'])
def index():
form = RegisterForm()
if form.validate_on_submit():
# everithing OK
return redirect(url_for('.index', _anchor='registration'))
# If form was submitted but contained invalid information
if form.is_submitted():
response = make_response(render_template('index.html', form=form))
return redirect(url_for('.index', _anchor='registration'), 302, response)
return render_template('index.html', form=form)
You can't send content with a redirect response, you can only say "go to this url". Also flask accepts a Response class, not an instance as a parameter for redirect.
To solve your problem, you need to use a session (or simply flashing) to preserve state across requests.
Here's a prototype:
from flask import Flask, render_template_string, request, session, redirect
from werkzeug import MultiDict
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField
from wtforms.validators import AnyOf
app = Flask(__name__)
app.secret_key = 'secret'
class MyForm(FlaskForm):
name = StringField('name', validators=[AnyOf(['secretname'])])
#app.route('/', methods=['POST', 'GET'])
def form_page():
form = MyForm()
html = '''
{% for error in form.name.errors %} <span>{{ error }}</span> {% endfor %}
<form method="POST" action="/">
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>
'''
if request.method == 'GET':
formdata = session.get('formdata', None)
if formdata:
form = MyForm(MultiDict(formdata))
form.validate()
session.pop('formdata')
return render_template_string(html, form=form)
if form.validate_on_submit():
# use the form somehow
# ...
return redirect('/#registered')
if form.is_submitted() and not form.validate():
session['formdata'] = request.form
return redirect('/#invalid')
if __name__ == "__main__":
app.run()
When you run the server, you get:
After you submit an invalid form, you get redirected to /#invalid and form is populated as you'd expect:
http://flask.pocoo.org/docs/1.0/quickstart/#sessions
http://flask.pocoo.org/docs/1.0/patterns/flashing/#message-flashing-pattern
http://flask.pocoo.org/docs/1.0/api/#flask.redirect

Put form input in variable (Python, Flask)

I am trying to retrieve the form input and put it in my variable (test), this is what I tried:
from flask import Flask, redirect, render_template, request, url_for
import random
app = Flask(__name__)
app.config["DEBUG"] = True
vraag = "Wat is een barkie?"
ans = ['€10', '€50', '€100', '€1000']
#app.route("/")
def Q1():
random.shuffle(ans)
return render_template('main_page.html', vraag=vraag, ans=ans)
#app.route("/1", methods=["GET", "POST"])
def A1():
test = request.POST
return render_template('check.html', test=test)
main_page.html:
<body>
<p>{{ vraag }}</p>
{% for item in ans%}
<form method="POST" action="{{ url_for('A1') }}">
<input type="submit" name={{ item }} value={{ item }} />
</form>
{% endfor %}
</body>
What am I doing wrong here? As error code it gives 'Unhandled Exception'
request.POST in A1 is not correct. Additionally you should wrap item in quotes.
A1 should look like:
#app.route("/1", methods=["GET", "POST"])
def A1():
if request.method == 'POST':
return render_template('check.html', test=str(request.form))
else:
return "This page doesn't accept GET requests."
request.form is an ImmutableMultiDict object, so you can get exact arguments passed from the form. The above example is only to show you what got passed to the form.

Determine which WTForms button was pressed in a Flask view

I have a page with multiple links to redirect the user to different pages. I thought using a form would be nicer, so I defined a WTForms Form with multiple SubmitFields. How do I determine which button was clicked and redirect based on that?
class MainForm(Form):
user_stats = SubmitField('User Stats')
room_stats = SubmitField('Room Stats')
#main.route('/')
#login_required
def index():
form = MainForm()
return render_template('index.html', form=form)
<form action="#" method="post">
{{ wtf.quick_form(form) }}
</form>
You added two buttons to the form, so check which of the fields' data is True.
from flask import Flask, render_template, redirect, url_for
from flask_wtf import Form
from wtforms import SubmitField
app = Flask(__name__)
app.secret_key = 'davidism'
class StatsForm(Form):
user_stats = SubmitField()
room_stats = SubmitField()
#app.route('/stats', methods=['GET', 'POST'])
def stats():
form = StatsForm()
if form.validate_on_submit():
if form.user_stats.data:
return redirect(url_for('user_stats'))
elif form.room_stats.data:
return redirect(url_for('room_stats'))
return render_template('stats.html', form=form)
app.run(debug=True)
<form method="post">
{{ form.hidden_tag() }}
{{ form.user_stats }}
{{ form.room_stats }}
</form>

Categories

Resources