Flask-WTForms How to override pre validate on Radio Fields - python

I'm trying to raise an error when a user submits without selecting a radio choice and it's not working. When I render the fields like this: {{ render_field(form.example) }} it prints the (self.gettext('PICK SOMETHING'), but when I use the format below nothing happens:
{% for subfield in form.religion %}
{{ render_field(subfield, class_="foo") }}
{{ render_field(subfield.label) }}
{% endfor %}
radio.py
class ReligionField(SelectField):
widget = ListWidget(prefix_label=False)
option_widget = RadioInput()
def pre_validate(self, form):
for v, _ in self.choices:
if self.data == v:
break
else:
raise ValueError(self.gettext('PICK SOMETHING'))
class ReligionForm(FlaskForm):
religions = ['Christian', 'Muslim', 'Hindu', 'Buddhist']
choices = [(x.lower(), x.title()) for x in sorted(religions)]
religion = ReligionField('Religion', validators=[DataRequired()], choices=choices, render_kw={"class":"post", "type":"radio"})
#app.route('/',methods=['post','get'])
def hello_world():
form = ReligionForm()
if form.validate_on_submit():
print form.religion.data
else:
print form.errors
return render_template('radio.html', form=form)
How can I override pre_validate to raise an error if I submit without selecting any choice?

Try this script:
from flask import Flask
from flask import render_template
from flask import request
from flask import flash
from flask import redirect
from flask_wtf import FlaskForm
from wtforms import SelectField
from wtforms.widgets import ListWidget, RadioInput
from wtforms.validators import DataRequired
app = Flask(__name__)
class ReligionField(SelectField):
widget = ListWidget(prefix_label=False)
option_widget = RadioInput()
def pre_validate(self, form):
for v, _ in self.choices:
if self.data == v:
break
else:
raise ValueError(self.gettext('PICK SOMETHING'))
class ReligionForm(FlaskForm):
religions = ['Christian', 'Muslim', 'Hindu', 'Buddhist']
choices = [(x.lower(), x.title()) for x in sorted(religions)]
religion = ReligionField('Religion', validators=[DataRequired()], choices=choices, render_kw={"class":"post", "type":"radio"})
#app.route('/',methods=['GET','POST'])
def hello_world():
form = ReligionForm(request.form)
if request.method == 'POST':
if form.validate_on_submit():
print form.religion.data
else:
print form.religion.errors[0]
flash(form.religion.errors[0])
return redirect('/')
return render_template('radio.html', form=form)
if __name__ == "__main__":
app.run()
radio.html
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class=flashes>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form method="post">
{% for subfield in form.religion %}
<li>{{subfield.label}} {{subfield}} </li>
{% endfor %}
<button>Submit</button>
</form>

Related

WTForms SelectField returning None

I'm using WTForms and Flask, I am trying to create a form where I can enter information about a recipe, but the product_name SelectField is returning None every time.
The form:
class CreateRecipeForm(Form):
product_name = SelectField(choices=get_craftables_options())
product_quantity = IntegerField(default=1)
job_field = SelectField(choices=['ALC', 'GSM', 'WVR'])
line_item_list = FieldList(FormField(RecipeLineForm), min_entries=6)
save_button = SubmitField()
The view:
#bp.route('/edit/new', methods=('GET', 'POST'))
def create_recipe():
form = CreateRecipeForm()
if request.method == 'POST':
selected_product = Item.query.get(form.product_name.data)
(do stuff here)
The template
{% block content %}
<form method="post">
{{ render_field(form.product_name) }}
{{ render_field(form.product_quantity) }}
{{ render_field_no_label(form.line_item_list) }}
{{ render_field_no_label(form.save_button) }}
</form>
{% endblock %}
I believe your issue lies in declaring the product_name. Make sure the get_craftables_options() is supposed to be a function and is returning a list of items compatible with the choices argument.
product_name = SelectField(choices=get_craftables_options())

Flask - commit FieldList to database using SQLAlchemy?

I am trying to commit fields generated from a FiedList but getting the error:
AttributeError: 'str' object has no attribute 'data'
What I'm trying to do is add a list of fields to the database which I can then retrieve and display on the page.
#App.py
#app.route('/', methods=['GET', 'POST'])
def index():
form = MainSubscriptionForm()
if form.validate_on_submit():
for x in form.subscription:
sub = Subscription(company=x.company.data, description=x.description.data)
db.session.add(sub)
db.session.commit()
elif request.method == 'GET':
list = Subscription.query.all()
return render_template('index.html', title="Home", form=form, list=list)
#forms.py
class SubscriptionForm(FlaskForm):
company = StringField(('Company'), validators=[DataRequired(), Length(min=0, max=20)])
description = StringField(('Description'), validators=[Length(min=0, max=120)])
save = SubmitField('Save')
class MainSubscriptionForm(FlaskForm):
subscription = FieldList(FormField(SubscriptionForm), min_entries=1)
#models.py
class Subscription(db.Model):
id = db.Column(db.Integer, primary_key=True)
company = db.Column(db.String(20))
description = db.Column(db.String(120))
#index.html
{% extends "base.html" %}
{% from 'bootstrap/form.html' import render_form_row %}
{% block content %}
<form method="post">
{{ form.csrf_token() }}
{% for sub in form.subscription %}
{{ render_form_row(sub) }}
{% endfor %}
</form>
{{ list }}
{% endblock %}
The issue was that a dictionary was being sent and therefore it was crashing on the below line:
sub = Subscription(company=x.company.data, description=x.description.data)
The solution was to get the key value as per below:
sub = Subscription(company=x.data['company'], description=x.data['description'])

Multiple forms in a single page using flask and WTForms in which one form link to another

This is what it looks like now:
This is what I need
I need two forms one linking to another in a same page.
On the first form: there will be an entry box for the user to fill out.
If the entry is valid the system should lead the user to second form.
The form would print out the result the system find and ask the user to enter a number into the entry box.
The system will see if the entry is valid. If it is it will do a seires of action()
(words in bold are the part that works)
My code:
main function
#app.route("/searchArea", methods=['GET', 'POST'])
#login_required
def searchArea():
if current_user.is_authenticated and verifyIdentity(current_user.username)==True:
form1 = FindArea()
form2 = SelectUser()
if form1.submit1.data and form1.validate():
allMatch = User.query.filter_by(area=form1.area1.data).all()
if(allMatch == []):
flash('area code does not exist', 'danger')
return redirect(url_for('searchArea'))
if form2.submit2.data and form2.validate(): #######
user_select = int(form2.area.data)
if(user_select>0 or user_select<=len(allMatch)):
user= allMatch[user_select-1]
author_name = user.username
posts = Post.query.filter_by(author=author_name).all()
emty_list = []
while(len(posts)!= emty_list):
db.session.delete(posts[0])
db.session.delete(user)
db.session.commit()
return redirect(url_for('home'))
flash('change have been made successfully', 'success')
#return redirect(url_for('deleteUser',user=allMatch[user_select]))
else:
return redirect(url_for('searchArea'))
flash('check your entry', 'danger')#######
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
return render_template('searchArea.html', title='Account',form=form1)
forms.py
class FindArea(FlaskForm):
area1=TextAreaField('Area Code', validators=[DataRequired()])
submit1 = SubmitField('Search')
class SelectUser(FlaskForm):
area2=TextAreaField('user number', validators=[DataRequired()])
submit2 = SubmitField('confirm')
template(searchArea.html)
{% extends "layout.html"%} {% block content %}
<div>
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.area1.label(class="form-control-label") }} {% if form.area1.errors %} {{ form.area1(class="form-control form-control-lg
is-invalid") }}
<div class="invalid-feedback">
{% for error in form.area1.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.area1(class="form-control form-control-lg") }}
{% endif %}
</div>
{{ form.submit1(class="btn btn-outline-info") }}
</form>
</div>
{% endblock content %}
template(print_area.html)
{% extends "layout.html"%} {% block content %}
<div>
<form method="POST" action="" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.area.label(class="form-control-label") }} {% if form.area.errors %} {{ form.area(class="form-control form-control-lg
is-invalid") }}
<div class="invalid-feedback">
{% for error in form.area.errors %}
<span>{{ error }}</span>
{% endfor %}
</div>
{% else %}
{{ form.area(class="form-control form-control-lg") }}
{% endif %}
</div>
{{ form.submit(class="btn btn-outline-info") }}
</form>
</div>
{% endblock content %}
Any help would be awsome!
Thanks!
When you click the submit button of form2, this condition(if form1.submit1.data and form1.validate():) is False.Maybe you need to change the code logic.
Okay so this is what I learn from this lesson. form.validate() and return render is a very tricky thing when it came to coding it run from top to bottm twice, first time when you enter the page it does not validate but render, second time it velidate; you can really do nothing to it except just add an variable to check if first form has been submitted, and return render for first when irst form has not been submitted and else return render for second. Here are my fixes of my code:
main function
#app.route("/searchArea", methods=['GET', 'POST'])
#login_required
def searchArea():
global allMatch
form1Passer = False
if current_user.is_authenticated and verifyIdentity(current_user.username)==True:
form1 = FindArea()
form2 = SelectUser()
if form1.submit1.data and form1.validate() and form1Passer == False: # notice the order
form1Passer == True
allMatch = User.query.filter_by(area=form1.area1.data).all()
if(allMatch == []):
flash('area code does not exist', 'danger')
return redirect(url_for('searchArea'))
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
if form2.submit2.data and form2.validate(): # notice the order
user_select = int(form2.area2.data)
if(user_select>0 and user_select<=len(allMatch)):
print("0")
user= allMatch[user_select-1]
print("1")
print("2")
posts = Post.query.filter_by(author=user).all()
print(type(posts))
while(True):
try:
db.session.delete(posts[0])
except:
break
db.session.delete(user)
db.session.commit()
return redirect(url_for('home'))
flash('change have been made successfully', 'success')
#return redirect(url_for('deleteUser',user=allMatch[user_select]))
else:
return redirect(url_for('searchArea'))
flash('check your entry', 'danger')
if form1Passer == False:
return render_template('searchArea.html', title='Account',form=form1)
else:
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
no modification need to be done. If you are having the same problem with me wish this would help you!
Edits: the code above totally works (but it's a poor practice) but when query returns a list the object in it is not linked to the object itself!
It's kinda like the adress of the supermarket!
When you change the adress you pu down wouldn't take affect to the actual location of the supermarket.
And when the superMarket moves, the adress would not automatic update itself!
using the filter method and .first() could avoid any oissible future problem! Since .first() is a direct controller of the object itself
This following code would also works when you only assigned a value to an object (ex: post_target.prperty = value) while the above will not(# is the line I modified):
allMatch=[] #
user; #
#app.route("/searchArea", methods=['GET', 'POST'])
#login_required
def searchArea():
global allMatch
global user
form1Passer = False
form2Passer = False
if current_user.is_authenticated and verifyIdentity(current_user.username)==True:
form1 = FindArea()
form2 = SelectUser()
form3 = ConfirmForm()
if form1.submit1.data and form1.validate() and form1Passer == False: # notice the order
form1Passer == True
if(form1.area1.data == "0"):
flash('this is an illegal move', 'danger')
return redirect(url_for('searchArea'))
allMatch = User.query.filter_by(area=form1.area1.data).all()
if(allMatch == []):
flash('area code does not exist', 'danger')
return redirect(url_for('searchArea'))
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
if form2.submit2.data and form2.validate() and form2Passer == False: # notice the order
user_select = int(form2.area2.data)
if(user_select>0 and user_select<=len(allMatch)):
user= allMatch[user_select-1]
return render_template('confirm.html', title='Account',user=user,form=form3)
else:
flash('check your entry', 'danger')
return redirect(url_for('searchArea'))
if form3.submit_cancel.data and form3.validate():
return redirect(url_for('searchArea'))
if form3.submit_confirm.data and form3.validate():
user_target = User.query.filter_by(email = user.email).first()#
while(True):
try:
post_target = Post.query.filter_by(author=user_target).first()#
db.session.delete(post_target)#
db.session.commit()
except:
break
db.session.delete(user_target)#
db.session.commit()
flash('change have been made successfully', 'success')
return redirect(url_for('home'))
#return redirect(url_for('deleteUser',user=allMatch[user_select]))
if form1Passer == False:
return render_template('searchArea.html', title='Account',form=form1)
elif form2Passer == False:
return render_template('print_area.html', title='Account',users=allMatch,form=form2)
else:
return render_template('confirm.html', title='Account',user=user,form=form3)
elif current_user.is_authenticated:
abort(403)
else:
return redirect(url_for('home'))

a flask web application that obtains two data from a user only accepts data in one field

I'm noob in Flask and Python.
It is my frstfsfs fsf fsfsfs
My goal is to create an web application.
There are 3 files in my application.
In the browswer there are 3 fields:
The sine2 of ------------ equals
The cossine2 of ------------ equals
sin2(x) + co2(x)------------ equals
Only the field sine2 works showing the value.
Nothing else works.
The goal is:
Enter a value in sine2 and click on the equal. The result of sine2 is showed.
The value appears in the field cosine2 and sin2+cos2. After the cliking on equal the cossine2 is showed.
Then click on the square sine to show the value.
The same applies when the cosine value is entered first.
visualizing in browser
controller.py:
from flask import Flask, render_template, request
from compute import *
from model import InputForm
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
form = InputForm(request.form)
if request.method == 'POST' and form.validate():
r = form.r.data
s = sin2(r)
else:
s = None
return render_template("view.html", form=form, s=s)
if request.method == 'POST' and form.validate():
d = form.r.data
e = cos2(d)
else:
e = None
return render_template("view.html", form=form, e=e)
if request.method == 'POST' and form.validate():
f = form.r.data
h = sin2cos2(f)
else:
h = None
return render_template("view.html", form=form, h=h)
if __name__ == '__main__':
app.run(debug=True)
compute.py
import math
def sin2(r):
return (math.sin(r)) ** 2
def cos2(r):
return (math.cos(r)) ** 2
def sin2cos2(r):
return (math.sin(r)) ** 2 + (math.cos(r)) ** 2
/template/view.html
<form method=post action="">
The sine2 of
{{ (form.r) }}
<input type=submit value=equals>
{% if s != None %}
{{ s }}
{% endif %}
<div>
The cossine2 of
{{ (form.r) }}
<input type=submit value=equals>
{% if e!= None %}
{{ e }}
{% endif %}
</div>
<div>
sin2(x)+cos2(x)
{{ (form.r) }}
<input type=submit value=equals>
{% if h!= None %}
{{ h }}
{% endif %}
</div>
</form>
After you return the function ends and any code written after is not ran. In your situation you have three return statements, so that is why only sine2 is working because everything after is never ran. Try removing the first two return statements and replacing the last return with:
return render_template("view.html", form=form, s=s, e=e, h=h)
Hope this helps!

Didnt return HttpResponse object. It returned None instead - django

I am getting an error with a view that i have and i was wondering if anyone can help me figure out where it is coming from. I am pretty sure it is something small that I am not seeing where it is coming from...
Within the view there will be a form that is displayed for the user to input informaiton, once the form is submitted, it is processed and then redirect to the users home...
Here is the error:
ValueError at /transfer/
The view tab.views.transfers didn't return an HttpResponse object. It returned None instead.
Request Method: POST
Request URL: http://localhost:8000/transfer/
Django Version: 1.8.6
Exception Type: ValueError
Exception Value:
The view tab.views.transfers didn't return an HttpResponse object. It returned None instead.
Here is the views.py
def transfers(request):
if 'username' not in request.session:
return redirect('login')
else:
username = request.session['username']
currentUser = User.objects.get(username = username)
if request.method == 'POST':
form = TransferForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
from_acct = cd['from_acct']
to_acct = cd['to_acct']
amount = cd['amount']
memo = cd['memo']
new_transfer = Transfers.objects.create(
user = currentUser,
from_acct = from_acct,
to_acct = to_acct,
amount = amount,
memo = memo,
frequency = 1,
status = 1,
)
return redirect('home_page')
else:
form = TransferForm()
form.fields['from_acct'].queryset = Accounts.objects.filter(user = currentUser).all()
message = 'please fill out the below form'
parameters = {
'form':form,
'currentUser':currentUser,
'message':message,
}
return render(request, 'tabs/user_balance.html', parameters)
Here is the html file:
{% extends "base.html" %}
{% block content %}
<h1>Transfer Money</h1>
{% if message %}
<p>{{message}}</p>
{% endif %}
<form action="." method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="submit" value="submit">
</form>
{% endblock %}
Here is the forms.py file portion
class TransferForm(forms.ModelForm):
acct_choices = (('tabz', 'Tabz - Username'),
('Wells Fargo', 'Wells Fargo - Username'))
from_acct = forms.TypedChoiceField(
choices=acct_choices, widget=forms.RadioSelect, coerce=int
)
to_acct = forms.TypedChoiceField(
choices=acct_choices, widget=forms.RadioSelect, coerce=int
)
class Meta:
model = Transfers
fields = ['from_acct', 'to_acct', 'amount', 'memo']
labels = {
'from_acct':'from',
'to_acct':'to',
}
from django.http import HttpResponse, HttpResponseRedirect
if request.method == 'POST':
form = TransferForm(request.POST)
if form.is_valid():
...
return HttpResponseRedirect(reverse_lazy('home'))
else:
form.fields['from_acct'].queryset = Accounts.objects.filter(user = currentUser).all()
message = 'please fill out the below form'
parameters = {
'form':form,
'currentUser':currentUser,
'message':message,
}
return render(request, 'tabs/user_balance.html', parameters)
html add form.errors
{% extends "base.html" %}
{% block content %}
<h1>Transfer Money</h1>
{% if message %}
<p>{{message}}</p>
{% endif %}
<form action='your_url/' method="POST">
{% csrf_token %}
{{ field.errors }}
{{ form.as_p }}
<input type="submit" name="submit" value="submit">
</form>
{% endblock %}
Well, this error should be thrown simply because you are giving an invalid form to your view. If you look at the logic of the view, if it is a POST and form is not valid the view does not return anything... well None for python. That's the error you are getting right?
Try to put an else statement with return after return redirect('home_page') and see if this fixes this part.

Categories