The following code returns "TypeError", the part of code that is causing the problem i think is "available_boilers = db.session.query(Boilers.name).all()boiler_list = [(b.name) for b in available_boilers]" where boiler list should be iterated over to form a drop down list in the form. I am using Flask-Bootstrap quick form to display the list so I dont think my problem is there, To check that a list is being sent I checked tis part of the code in shell
@bp.route('/control/addboiler_circuit', methods=('GET', 'POST'))
def addboiler_circuit():
available_boilers = db.session.query(Boilers.name).all()
boiler_list = [(b.name) for b in available_boilers]
form = AddBoiler_CircuitForm()
form.name.choices = boiler_list
if form.validate_on_submit():
boiler_circuits = Boilers_Circuit(name=form.name.data,\
BoilerName=form.BoilerName.data)
db.session.add(boilers_circuits)
db.session.commit()
flash('Congratulations, you have now a registered a new boiler
circuit!')
return redirect(url_for('control.addboiler_circuit'))
return render_template('control/addboiler_circuit.html', title='Add boiler',
form=form)
form.py
class AddBoiler_CircuitForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
BoilerName = SelectField('BoilerName', coerce=int, validators=[DataRequired()])
def validate_boilerName(self, boilerName):
boilers = Boilers_Circuit.query.filter_by(boiler=BoilerName.data).first()
if boilers is not None:
raise ValidationError('Please use a different Boiler.')
python shell
>>> available_boilers = db.session.query(Boilers.name).all()
>>> print(available_boilers)
[('Log Burner',), ('solar panel',), ('pellet boiler',)]
>>> boiler_list = [(b.name) for b in available_boilers]
>>> print(boiler_list)
['Log Burner', 'solar panel', 'pellet boiler']
My code comes from WTForms and from Dynamic choices WTForms Flask SelectField
This is new territory for me and I am a in need of help
EDIT Here is the complete error message
> Traceback (most recent call last):
1/File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py",
line 2309, in __call__
return self.wsgi_app(environ, start_response)
2/ File "/home/pi/heating/venv/lib/python3.7/site-
packages/flask/app.py",
line 2295, in wsgi_app
response = self.handle_exception(e)
3/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
4/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
5/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
6/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
7/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
8/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
9/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
10/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
11/ File "/home/pi/heating/homeHeating/control/control.py", line 49, in addboiler_circuit
form=form)
12/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/templating.py", line 135, in render_template
context, ctx.app)
13/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask/templating.py", line 117, in _render
rv = template.render(context)
14/ File "/home/pi/heating/venv/lib/python3.7/site-packages/jinja2/asyncsupport.py", line 76, in render
return original_render(self, *args, **kwargs)
15/ File "/home/pi/heating/venv/lib/python3.7/site-packages/jinja2/environment.py", line 1008, in render
return self.environment.handle_exception(exc_info, True)
16/ File "/home/pi/heating/venv/lib/python3.7/site-packages/jinja2/environment.py", line 780, in handle_exception
reraise(exc_type, exc_value, tb)
17/ File "/home/pi/heating/venv/lib/python3.7/site-packages/jinja2/_compat.py", line 37, in reraise
raise value.with_traceback(tb)
18/ File "/home/pi/heating/homeHeating/templates/control/addboiler_circuit.html", line 2, in top-level template code
{% import 'bootstrap/wtf.html' as wtf %}
19/ File "/home/pi/heating/homeHeating/templates/base.html", line 1,
in top-level template code {% extends 'bootstrap/base.html' %}
20/ File "/home/pi/heating/venv/lib/python3.7/site-
packages/flask_bootstrap/templates/bootstrap/base.html", line 1, in
top-level template code
{% block doc -%}
21/ File "/home/pi/heating/venv/lib/python3.7/site-
packages/flask_bootstrap/templates/bootstrap/base.html", line 4, in
block "doc"
{%- block html %}
21/ File "/home/pi/heating/venv/lib/python3.7/site-
packages/flask_bootstrap/templates/bootstrap/base.html", line 20, in
block
"html"
{% block body -%}
22/ File "/home/pi/heating/venv/lib/python3.7/site-
packages/flask_bootstrap/templates/bootstrap/base.html", line 23, in
block "body"
{% block content -%} 23/File"/home/pi/heating/homeHeating/templates/control/addboiler_circuit.html", line 8, in block "content" {{ wtf.quick_form(form) }}
24/ File "/home/pi/heating/venv/lib/python3.7/site-packages/jinja2/runtime.py", line 579, in _invoke rv = self._func(*arguments)
25/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask_bootstrap/templates/bootstrap/wtf.html", line 205, in template {{ form_field(field,
26/ File "/home/pi/heating/venv/lib/python3.7/site-packages/jinja2/runtime.py", line 579, in _invoke rv = self._func(*arguments)
27/ File "/home/pi/heating/venv/lib/python3.7/site-packages/flask_bootstrap/templates/bootstrap/wtf.html", line 123, in template {{field(class="form-control", **kwargs)|safe}}
28/ File "/home/pi/heating/venv/lib/python3.7/site packages/wtforms/fields/core.py", line 155, in __call__ return self.meta.render_field(self, kwargs)
29/ File "/home/pi/heating/venv/lib/python3.7/site packages/wtforms/meta.py", line 56, in render_field return field.widget(field, **render_kw)
30/ File "/home/pi/heating/venv/lib/python3.7/site packages/wtforms/widgets/core.py", line 323, in __call__ for val, label, selected in field.iter_choices():
31/ File "/home/pi/heating/venv/lib/python3.7/site packages/wtforms/fields/core.py", line 454, in iter_choices for value, label in self.choices:
TypeError: 'NoneType' object is not iterable
This is the full error script, Line 23/ mentions my form "addboiler_circuit.html" which I have added below:
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block content %}
<h1>{{ 'Register' }}</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
{% endblock %}
Thank you for taking a look at my problem regards Paul
EDIT 30/03/2019 This is a bit more complicated than I had shown, I eventually finished with this:
@bp.route('/control/addboiler_circuit', methods=('GET', 'POST'))
def addboiler_circuit():
boiler_list = [(b.id, b.id) for b in db.session.query(Boilers).all()]
sensorID_list = [(s.id, s.sensorID) for s in db.session.query(Sensors).all()]
valveID_list = [(p.id, p.name) for p in db.session.query(Pins).all()]
form = AddBoiler_CircuitForm()
form.boiler_ID.choices = boiler_list
form.sensor_ID1.choices = sensorID_list
form.sensor_ID2.choices = sensorID_list
form.sensor_ID3.choices = sensorID_list
form.pin_ID1.choices = valveID_list
form.pin_ID2.choices = valveID_list
form.pin_ID3.choices = valveID_list
if form.validate_on_submit():
boilercircuits = Boilercircuit(name=form.name.data,\
boiler_ID=form.boiler_ID.data,sensor_ID1=form.sensor_ID1.data,\
sensor_ID2=form.sensor_ID2.data,sensor_ID3=form.sensor_ID3.data,\
pin_ID1=form.pin_ID1.data,pin_ID2=form.pin_ID2.data,\
pin_ID3=form.pin_ID3.data)
db.session.add(boilercircuits)
db.session.commit()
flash('Congratulations, you have now a registered a new boiler circuit!')
return redirect(url_for('control.addboiler_circuit'))
return render_template('control/addboiler_circuit.html', title='Add boiler',
form=form)
Now @sleblanc has come up with a whole new way to write this function, but how do I put that into place? I have so many questions I dont know where to begin. Thank you for your help.
31/ File "/home/pi/heating/venv/lib/python3.7/site packages/wtforms/fields/core.py", line 454, in iter_choices for value, label in self.choices:
TypeError: 'NoneType' object is not iterable
This tells us that your form does not have the choices set for the boilerName field. In your code, it seems like you are doing it, but upon closer inspection, you are setting choices on the name field rather than the boilerName field.
There are other issues with your code, too. Choices should be a list of (id, 'label') tuples. If your model refers to a literal string, then you may simply do [(b.name, b.name) for b in available_boilers].
Also, since you are using flask_wtf, you should rewrite your views this way to make them clearer:
@bp.route('/control/addboiler_circuit', methods=('GET', 'POST'))
def addboiler_circuit():
available_boilers = Boilers.query.all()
boiler_list = [(b.name) for b in available_boilers]
obj = Circuit() # you may pass default parameters to this constructor
form = AddBoiler_CircuitForm(request.form, obj=obj)
form.name.choices = boiler_list
if form.validate_on_submit():
form.populate(obj)
db.session.add(obj)
db.session.commit()
flash('Congratulations, you have now a registered a new '
'boiler circuit!')
return redirect(url_for('control.addboiler_circuit'))
return render_template('control/addboiler_circuit.html', title='Add boiler',
form=form)
Furthermore, consider using WTForms-SQLAlchemy's QuerySelectField to populate the choices, it saves you from some more boilerplate:
from wtforms_sqlalchemy.fields import QuerySelectField
class AddBoiler_CircuitForm(FlaskForm):
name = StringField('Name', validators=[DataRequired()])
boiler = QuerySelectField(
'Boiler name',
query_factory=lambda: Boiler.query.all(),
validators=[DataRequired()])
By setting a relationship named "boiler" on your Circuit model, the form's populate_obj will do exactly what you expect it to do. QuerySelectField will even ensure that the user's choice is contained in the query that you provide it, thus preventing crafty users from editing the HTML form and submitting disallowed values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With