【问题标题】:How to make Flask-WTFoms update labels dynamically from list of label names?如何使 Flask-WTForms 从标签名称列表中动态更新标签?
【发布时间】:2018-05-08 21:16:53
【问题描述】:

我使用 WTForms 为数据过滤定义表单,它的定义如下(我的目标是为 BooleanFields 设置用户指定的标签,我让每个用户为字段命名标签并将字段名称保存到 Google Datastore):

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 类返回代表布尔字段所需标签的字符串列表(这些值存储在 Google 数据存储中)。

这个表单展示在Jinja2和Flask渲染的dashboard主页上(这里只贴Flask类的相关部分):

@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)

在第一次运行时,所有标签都已正确设置和显示。但是当我更改 Datastore 中的值(通过另一个表单)时,表单标签中的值永远不会更新,它们保持不变,除非我重新启动网络服务器。

我尝试将“调试”打印到程序的不同部分并输出从 Datastore 读取数据的类,并且输出始终有效并且与预期值同步。在我看来(对我来说这完全是魔法),

form = MainFilterForm()

在第一次 HTTP 请求时仅执行一次(因为我也尝试将“调试”打印到 MainFilterForm 定义,但此打印仅在第一次 HTTP 请求时显示)。

我尝试手动设置标签:

form.selected_colors_calendar_color.label = calendar_colors_descriptions[0]

行后:

form = MainFilterForm()

但我相信来自 Jinja2 的错误“TypeError: 'str' object is not callable”。

【问题讨论】:

    标签: python google-app-engine flask jinja2 wtforms


    【解决方案1】:

    您采用的方法calendar_colors_descriptions 分配在表单类的主体中。

    这意味着它只被评估一次 - 当表单模块首次导入时 - 因此字段标签值是固定的,直到服务器重新启动。实际上,标签值是 class 定义的一部分,并且在类的所有 instances 中都是通用的。

    此示例代码与您的类似;

    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">
    

    【讨论】:

    • 嗨,很抱歉这么晚才回复。只要我需要一个快速的解决方案,我就做了一些讨厌的非系统解决方案。我刚刚尝试使用 def init 构造来解决另一个问题,它似乎运行良好。我只是做了一个小的修正。 self['foo'].label 语法对我不起作用。但是 self.foo.label 效果很好。
    【解决方案2】:

    如下创建示例表单:

    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")
    

    【讨论】:

    • 我认为你不需要那个:form.name.label.text = 'New label'
    • @anvd 你能分享更多的光吗?我很高兴学习。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 2017-01-12
    • 2016-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    相关资源
    最近更新 更多