如何使 Flask-WTForms 从标签名称列表中动态更新标签?
How to make Flask-WTFoms update labels dynamically from list of label names?
我使用 WTForms 来定义数据过滤的表单,它是这样定义的(我的目标是为 BooleanFields 设置用户指定的标签,我让每个用户为字段命名标签,并将字段名称保存到 Google 数据存储):
class MainFilterForm(FlaskForm):
"""
Represents main filter form.
"""
start_date = pendulum.parse(
str(pendulum.today().year)
+ str(pendulum.today().month)
+ '01')
end_date = pendulum.today()
calendar_colors_descriptions = CalendarColorsDescription(
users.get_current_user().user_id()
).get_colors_description()
search_query = StringField(
'Search',
[
validators.Length(min=1, max=128),
validators.optional()],
default=None)
start_date = DateField(
'Start date',
[validators.required()],
format='%Y-%m-%d',
default=start_date)
end_date = DateField(
'End date',
[validators.required()],
format='%Y-%m-%d',
default=end_date)
i_am_owner = BooleanField(
'I am owner',
default=False)
include_all_day_events = BooleanField(
'Include all day events',
default=False)
selected_colors_calendar_color = BooleanField(
calendar_colors_descriptions[0],
default=True)
selected_colors_color1 = BooleanField(
calendar_colors_descriptions[1],
default=True)
selected_colors_color2 = BooleanField(
calendar_colors_descriptions[2],
default=True)
selected_colors_color3 = BooleanField(
calendar_colors_descriptions[3],
default=True)
selected_colors_color4 = BooleanField(
calendar_colors_descriptions[4],
default=True)
selected_colors_color5 = BooleanField(
calendar_colors_descriptions[5],
default=True)
selected_colors_color6 = BooleanField(
calendar_colors_descriptions[6],
default=True)
selected_colors_color7 = BooleanField(
calendar_colors_descriptions[7],
default=True)
selected_colors_color8 = BooleanField(
calendar_colors_descriptions[8],
default=True)
selected_colors_color9 = BooleanField(
calendar_colors_descriptions[9],
default=True)
selected_colors_color10 = BooleanField(
calendar_colors_descriptions[10],
default=True)
selected_colors_color11 = BooleanField(
calendar_colors_descriptions[11],
default=True)
CalendarColorsDescription class returns 表示布尔字段所需标签的字符串列表(这些值存储在 Google 数据存储中)。
此表单显示在 Jinja2 和 Flask 呈现的仪表板主页上(此处仅粘贴 Flask class 的相关部分):
@APP.route('/dashboard', methods=('GET', 'POST'))
def dashboard():
"""
Main page handler, shows stats dashboard.
"""
form = MainFilterForm()
calendar_events = get_events(
calendar_service,
form.search_query.data,
form.start_date.data,
form.end_date.data,
form.i_am_owner.data,
form.include_all_day_events.data,
form.selected_colors_calendar_color.data,
form.selected_colors_color1.data,
form.selected_colors_color2.data,
form.selected_colors_color3.data,
form.selected_colors_color4.data,
form.selected_colors_color5.data,
form.selected_colors_color6.data,
form.selected_colors_color7.data,
form.selected_colors_color8.data,
form.selected_colors_color9.data,
form.selected_colors_color10.data,
form.selected_colors_color11.data)
return flask.render_template(
'dashboard.html',
calendar_events=calendar_events,
form=form)
首先 运行 所有标签都已正确设置和显示。但是当我更改数据存储中的值(通过另一种形式)时,表单标签中的值永远不会更新它们保持不变,除非我重新启动网络服务器。
我尝试将 "debug" 打印到程序的不同部分并输出从 Datastore 读取数据的 class,并且输出始终有效并与预期值同步。在我看来(对我来说这完全是魔法),
form = MainFilterForm()
仅在第一次 HTTP 请求时执行一次(因为我也尝试将 "debug" 打印到 MainFilterForm 定义中,但此打印仅在第一次 HTTP 请求时显示)。
我尝试手动设置标签:
form.selected_colors_calendar_color.label = calendar_colors_descriptions[0]
行后:
form = MainFilterForm()
但我收到错误 "TypeError: 'str' object is not callable",我相信是来自 Jinja2。
您所采用的方法 calendar_colors_descriptions
已分配到您的表单正文中 class。
这意味着它只被评估一次——当第一次导入表单模块时——因此字段标签值是固定的,直到服务器重新启动。实际上,标签值是 class 定义的一部分,因此在 class 的所有 实例 中都很常见.
此示例代码与您的相似;
import random
import wtforms
def get_labels(labels=None):
if labels is None:
labels = ['red', 'amber', 'green']
# Simulate data changes by shuffling the list.
random.shuffle(labels)
return labels
class StaticLabelForm(wtforms.Form):
# labels is set when the class is compiled at import time.
labels = get_labels()
foo = wtforms.BooleanField(labels[0], default=True)
bar = wtforms.BooleanField(labels[1], default=True)
baz = wtforms.BooleanField(labels[2], default=True)
每次我们实例化一个新的StaticLabelForm
,标签总是相同的,因为get_labels
函数只被调用一次
>>> static1 = StaticLabelForm()
>>> for field in static1: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">green</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">red</label> <input checked id="baz" name="baz" type="checkbox" value="y">
>>> static2 = StaticLabelForm()
>>> for field in static2: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">green</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">red</label> <input checked id="baz" name="baz" type="checkbox" value="y">
我们可以通过将标签值传递给表单的 __init__
方法,并将它们设置在 __init__
方法内的字段上来解决这个问题。
class DynamicLabelForm(wtforms.Form):
# Don't set the labels here
foo = wtforms.BooleanField(default=True)
bar = wtforms.BooleanField(default=True)
baz = wtforms.BooleanField(default=True)
def __init__(self, labels=None, **kwargs):
super().__init__(**kwargs)
# super(DynamicLabelForm, self).__init__(**kwargs) for python2!
if labels is None:
labels = ['red', 'amber', 'green']
self['foo'].label = wtforms.Label(self['foo'].id, labels[0])
self['bar'].label = wtforms.Label(self['bar'].id, labels[1])
self['baz'].label = wtforms.Label(self['baz'].id, labels[2])
现在每个新表单上的标签都已重置:
>>> dynamic1 = DynamicLabelForm(labels=get_labels())
>>> for field in dynamic1: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">red</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">green</label> <input checked id="baz" name="baz" type="checkbox" value="y">
>>> dynamic2 = DynamicLabelForm(labels=get_labels())
>>> for field in dynamic2: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">green</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">red</label> <input checked id="baz" name="baz" type="checkbox" value="y">
创建示例表单如下:
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired, Length
from wtforms.fields import Label #<==This is the key
#in forms.py
class MyForm(FlaskForm):
name = StringField('Your Name', validators=[DataRequired(), Length(min=2, max=20)])
submit = SubmitField('Sign Up')
#in route.py
from forms import MyForm
#initialize your app
@app.route("/some-test-route", methods = ["GET","POST"])
def someTestRoute():
form = MyForm
if some condition:
#change the label as follows.
form.name.label = Label(field_id = "name", text = "Your New Field Description Goes Here.")
#The above line creates the following lable object
#<label for="name">Your New Field Description Goes Here.</label>
#which replaces the current label object that is
#<label for="name">yourname</label>
if form.validate_on_submit():
#do something
return redirect(url_for(endpoint = "someEndPoint"))
return render_template("WhereEverYour_MyForm_Template.html")
我使用 WTForms 来定义数据过滤的表单,它是这样定义的(我的目标是为 BooleanFields 设置用户指定的标签,我让每个用户为字段命名标签,并将字段名称保存到 Google 数据存储):
class MainFilterForm(FlaskForm):
"""
Represents main filter form.
"""
start_date = pendulum.parse(
str(pendulum.today().year)
+ str(pendulum.today().month)
+ '01')
end_date = pendulum.today()
calendar_colors_descriptions = CalendarColorsDescription(
users.get_current_user().user_id()
).get_colors_description()
search_query = StringField(
'Search',
[
validators.Length(min=1, max=128),
validators.optional()],
default=None)
start_date = DateField(
'Start date',
[validators.required()],
format='%Y-%m-%d',
default=start_date)
end_date = DateField(
'End date',
[validators.required()],
format='%Y-%m-%d',
default=end_date)
i_am_owner = BooleanField(
'I am owner',
default=False)
include_all_day_events = BooleanField(
'Include all day events',
default=False)
selected_colors_calendar_color = BooleanField(
calendar_colors_descriptions[0],
default=True)
selected_colors_color1 = BooleanField(
calendar_colors_descriptions[1],
default=True)
selected_colors_color2 = BooleanField(
calendar_colors_descriptions[2],
default=True)
selected_colors_color3 = BooleanField(
calendar_colors_descriptions[3],
default=True)
selected_colors_color4 = BooleanField(
calendar_colors_descriptions[4],
default=True)
selected_colors_color5 = BooleanField(
calendar_colors_descriptions[5],
default=True)
selected_colors_color6 = BooleanField(
calendar_colors_descriptions[6],
default=True)
selected_colors_color7 = BooleanField(
calendar_colors_descriptions[7],
default=True)
selected_colors_color8 = BooleanField(
calendar_colors_descriptions[8],
default=True)
selected_colors_color9 = BooleanField(
calendar_colors_descriptions[9],
default=True)
selected_colors_color10 = BooleanField(
calendar_colors_descriptions[10],
default=True)
selected_colors_color11 = BooleanField(
calendar_colors_descriptions[11],
default=True)
CalendarColorsDescription class returns 表示布尔字段所需标签的字符串列表(这些值存储在 Google 数据存储中)。
此表单显示在 Jinja2 和 Flask 呈现的仪表板主页上(此处仅粘贴 Flask class 的相关部分):
@APP.route('/dashboard', methods=('GET', 'POST'))
def dashboard():
"""
Main page handler, shows stats dashboard.
"""
form = MainFilterForm()
calendar_events = get_events(
calendar_service,
form.search_query.data,
form.start_date.data,
form.end_date.data,
form.i_am_owner.data,
form.include_all_day_events.data,
form.selected_colors_calendar_color.data,
form.selected_colors_color1.data,
form.selected_colors_color2.data,
form.selected_colors_color3.data,
form.selected_colors_color4.data,
form.selected_colors_color5.data,
form.selected_colors_color6.data,
form.selected_colors_color7.data,
form.selected_colors_color8.data,
form.selected_colors_color9.data,
form.selected_colors_color10.data,
form.selected_colors_color11.data)
return flask.render_template(
'dashboard.html',
calendar_events=calendar_events,
form=form)
首先 运行 所有标签都已正确设置和显示。但是当我更改数据存储中的值(通过另一种形式)时,表单标签中的值永远不会更新它们保持不变,除非我重新启动网络服务器。
我尝试将 "debug" 打印到程序的不同部分并输出从 Datastore 读取数据的 class,并且输出始终有效并与预期值同步。在我看来(对我来说这完全是魔法),
form = MainFilterForm()
仅在第一次 HTTP 请求时执行一次(因为我也尝试将 "debug" 打印到 MainFilterForm 定义中,但此打印仅在第一次 HTTP 请求时显示)。
我尝试手动设置标签:
form.selected_colors_calendar_color.label = calendar_colors_descriptions[0]
行后:
form = MainFilterForm()
但我收到错误 "TypeError: 'str' object is not callable",我相信是来自 Jinja2。
您所采用的方法 calendar_colors_descriptions
已分配到您的表单正文中 class。
这意味着它只被评估一次——当第一次导入表单模块时——因此字段标签值是固定的,直到服务器重新启动。实际上,标签值是 class 定义的一部分,因此在 class 的所有 实例 中都很常见.
此示例代码与您的相似;
import random
import wtforms
def get_labels(labels=None):
if labels is None:
labels = ['red', 'amber', 'green']
# Simulate data changes by shuffling the list.
random.shuffle(labels)
return labels
class StaticLabelForm(wtforms.Form):
# labels is set when the class is compiled at import time.
labels = get_labels()
foo = wtforms.BooleanField(labels[0], default=True)
bar = wtforms.BooleanField(labels[1], default=True)
baz = wtforms.BooleanField(labels[2], default=True)
每次我们实例化一个新的StaticLabelForm
,标签总是相同的,因为get_labels
函数只被调用一次
>>> static1 = StaticLabelForm()
>>> for field in static1: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">green</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">red</label> <input checked id="baz" name="baz" type="checkbox" value="y">
>>> static2 = StaticLabelForm()
>>> for field in static2: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">green</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">red</label> <input checked id="baz" name="baz" type="checkbox" value="y">
我们可以通过将标签值传递给表单的 __init__
方法,并将它们设置在 __init__
方法内的字段上来解决这个问题。
class DynamicLabelForm(wtforms.Form):
# Don't set the labels here
foo = wtforms.BooleanField(default=True)
bar = wtforms.BooleanField(default=True)
baz = wtforms.BooleanField(default=True)
def __init__(self, labels=None, **kwargs):
super().__init__(**kwargs)
# super(DynamicLabelForm, self).__init__(**kwargs) for python2!
if labels is None:
labels = ['red', 'amber', 'green']
self['foo'].label = wtforms.Label(self['foo'].id, labels[0])
self['bar'].label = wtforms.Label(self['bar'].id, labels[1])
self['baz'].label = wtforms.Label(self['baz'].id, labels[2])
现在每个新表单上的标签都已重置:
>>> dynamic1 = DynamicLabelForm(labels=get_labels())
>>> for field in dynamic1: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">red</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">green</label> <input checked id="baz" name="baz" type="checkbox" value="y">
>>> dynamic2 = DynamicLabelForm(labels=get_labels())
>>> for field in dynamic2: print(field.label, field)
...
<label for="foo">amber</label> <input checked id="foo" name="foo" type="checkbox" value="y">
<label for="bar">green</label> <input checked id="bar" name="bar" type="checkbox" value="y">
<label for="baz">red</label> <input checked id="baz" name="baz" type="checkbox" value="y">
创建示例表单如下:
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired, Length
from wtforms.fields import Label #<==This is the key
#in forms.py
class MyForm(FlaskForm):
name = StringField('Your Name', validators=[DataRequired(), Length(min=2, max=20)])
submit = SubmitField('Sign Up')
#in route.py
from forms import MyForm
#initialize your app
@app.route("/some-test-route", methods = ["GET","POST"])
def someTestRoute():
form = MyForm
if some condition:
#change the label as follows.
form.name.label = Label(field_id = "name", text = "Your New Field Description Goes Here.")
#The above line creates the following lable object
#<label for="name">Your New Field Description Goes Here.</label>
#which replaces the current label object that is
#<label for="name">yourname</label>
if form.validate_on_submit():
#do something
return redirect(url_for(endpoint = "someEndPoint"))
return render_template("WhereEverYour_MyForm_Template.html")