wtforms field created through setattr loses properties

2024-06-25 05:58:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图用python/Flask创建一个表单,它将向一组标准字段添加一些动态滑块输入。不过,我正在努力让它正常工作。在

我的应用程序中的大多数web表单都是静态的,通过wtforms创建,如:

    class CritiqueForm(Form):

        rating = IntegerField('Rating')
        comment = TextAreaField('Comments')
        submit = SubmitField('Save Critique')

当我像这样显式的时候,我可以通过在视图中使用CritiqueForm()并传递要在模板中呈现的form对象来获得预期的结果。在

然而,我有一个评论表单,它需要动态地包含一些特定于特定记录的评分标准的滑块。滑动条的数量可以随记录的不同而变化,文本和ID也会随记录的关联条件而变化。在

当我寻找一些方法来处理这个问题时,我从dezza(Dynamic forms from variable length elements: wtforms)找到了一个可能的解决方案,方法是在窗体中创建一个类方法,然后在实例化要呈现的窗体之前调用它。如:

^{pr2}$

其中“append_slider”总是带有我提供的标签的IntegerField。这足以让我在视图中填充条件滑块,如:

    @app.route('/critique/<url_id>/edit', methods=['GET', 'POST'])
    def edit_critique(url_id):
        from app.models import RecordModel
        from app.models.forms import CritiqueForm

        record = RecordModel.get_object_by_url_id(url_id)
        if not record: abort(404)

        # build editing form
        ratings = list()
        for i, criterium in enumerate(record.criteria):
            CritiqueForm.append_slider('rating_' + str(i+1),criterium.name)
            ratings.append('form.rating_' + str(i+1))
        form = CritiqueForm(request.form)

        # Process valid POST
        if request.method=='POST' and form.validate():
           # Process the submitted form and show updated read-only record        
            return render_template('critique.html')

        # Display edit form
        return render_template('edit_critique.html',
            form=form,
            ratings=ratings,
            )

构建ratings列表的目的是为模板提供一种引用动态字段的简单方法:

    {% for rating_field in ratings %}
        {{ render_slider_field(rating_field, label_visible=True, default_value=0) }} 
    {% endfor %}

其中render_slider_field是一个宏,用于将整数字段转换为滑块。在

使用form.rating(一个在CritiqueForm中显式定义的整型字段)没有问题,滑块是按预期生成的。但是,对于动态整数字段,我不能引用整型字段中的label值。堆栈跟踪的最后一部分如下所示:

    File "/home/vagrant/msp/app/templates/edit_critique.html", line 41, in block "content"
    {{ render_slider_field(rating_field, label_visible=True, default_value=0) }}

    File "/home/vagrant/msp/app/templates/common/form_macros.html", line 49, in template
    {% set label = kwargs.pop('label', field.label.text) %}

    File "/home/vagrant/.virtualenvs/msp/lib/python2.7/site-packages/jinja2/environment.py", line 397, in getattr
    return getattr(obj, attribute)

    UndefinedError: 'str object' has no attribute 'label'

通过一些调试,我确认没有出现任何预期的字段属性(例如,name、short\u name、id…)。当尘埃落定,我只想要这个:

        CritiqueForm.append_slider('rating', 'Rating')

相当于:

        rating = IntegerField('Rating')

setattr()技术是否固有地限制了表单中可以包含的信息,还是我只是错误地初始化或引用了字段属性?在

编辑: 两个变化允许我的即时拦截器被移除。在

1)我引用模板中的表单字段不正确。字段参数(例如,标签)出现在该更改的预期位置:

    {% for rating_field in ratings %}
            {{ render_slider_field(form[rating_field], label_visible=True, default_value=0) }} 
    {% endfor %}

其中我将字符串rating_field替换为form[rating_field]。在

2)为了解决从视图动态更改基类的问题,创建了一个新的表单类ThisForm()来扩展我的基类CritiqueForm,然后在那里进行动态追加:

    class ThisForm(CritiqueForm):
        pass

    # build criteria form fields
    ratings = list()
    for i, criterium in enumerate(record.criteria):
        setattr(ThisForm, 'rating_' + str(i+1), IntegerField(criterium.name))
        ratings.append('rating_' + str(i+1))

    form = ThisForm(request.form)

我不知道这是否解决了评论中提到的预期性能和数据完整性问题,但至少这似乎是朝着正确方向迈出的一步。在


Tags: informapp表单field动态renderlabel
1条回答
网友
1楼 · 发布于 2024-06-25 05:58:10

setattr(obj, name, value)obj.name = value的完全等价物,两者都是obj.__setattr__(name, value)的语法糖,所以你的问题不是{}的“某些限制”,而是wtform.Form是如何工作的。如果你看一下source code,你会发现除了将字段声明为类属性(涉及元类魔术…)之外,还有很多事情可以让字段和表单协同工作。接下来,您必须通读源代码,了解如何向表单动态添加字段。在

另外,您的代码会尝试在类本身上设置新字段。在具有并发访问的多进程/多线程/长时间运行的流程环境中,这是一个很大的禁忌—每个请求都将修改(在进程级别共享)表单类,不小心添加或重写字段。它似乎可以在单进程单线程的开发服务器上工作,但会在生产中中断,并产生最不可预测的错误或(更糟的)错误结果。在

因此,您真正想要了解的是如何将字段动态地添加到form实例中,或者,作为替代,如何动态地构建一个新的临时表单类(这远不是很困难,请记住Python类也是对象)。在

相关问题 更多 >