以下代码返回“ TypeError”,这是导致我认为是问题的代码部分 “ available_boilers = db.session.query(Boilers.name).all()boiler_list = [available_boilers中b的(b.name)]” 在锅炉列表中应进行迭代以在表单中形成一个下拉列表。我正在使用Flask-Bootstrap快速表单显示列表,所以我不认为我的问题在那里,要检查是否发送了列表,我检查了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']
我的代码来自WTForms和Dynamic choices WTForms Flask SelectField
这对我来说是新领域,我需要帮助
编辑 这是完整的错误消息
> 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
这是完整的错误脚本,第23行/提到我在下面添加的表单“ addboiler_circuit.html”:
{% 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 %}
谢谢你看我的问题 问候保罗
编辑30/03/2019 这比我之前显示的要复杂一些,我最终完成了这一点:
@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)
现在@sleblanc提出了一种全新的方式来编写此函数,但是我该如何实现呢?我有很多问题,我不知道从哪里开始。谢谢您的帮助。
答案 0 :(得分:1)
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
这告诉我们您的表单没有为锅炉名称字段设置选项。在您的代码中,您似乎正在执行此操作,但是在仔细检查后,您是在name
字段而不是boilerName
字段上设置选项。
您的代码也存在其他问题。 Choices
应该是(id, 'label')
元组的列表。如果您的模型引用文字字符串,那么您可以简单地进行[(b.name, b.name) for b in available_boilers]
。
此外,由于您使用的是flask_wtf,因此应以这种方式重写视图,以使它们更清晰:
@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)
此外,考虑使用WTForms-SQLAlchemy的QuerySelectField来填充选择,这可以使您免于重复:
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()])
通过在电路模型上设置一个名为“锅炉”的关系,表单的populate_obj将完全执行您期望的操作。 QuerySelectField甚至可以确保用户的选择包含在您提供的查询中,从而防止狡猾的用户编辑HTML表单并提交不允许的值。