Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: 'NoneType' object is not iterable WTForms FieldSelect

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.

like image 231
pascale Avatar asked Aug 02 '26 07:08

pascale


1 Answers

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.

like image 52
sleblanc Avatar answered Aug 04 '26 01:08

sleblanc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!