代码之家  ›  专栏  ›  技术社区  ›  daveruinseverything

从用户界面动态添加新的WTForms字段列表项

  •  9
  • daveruinseverything  · 技术社区  · 6 年前

    我有一个flask+wtforms应用程序,我希望用户能够同时输入父对象和任意数量的子对象。我不确定从用户界面动态创建新的子表单输入字段的最佳方法。

    到目前为止我所拥有的

    下面是一个完整的工作示例。(注意:这是一个人工示例,用来突出显示单个.py文件中的所有工作部分,这会生成一些相当混乱的代码。对不起。)

    from flask import Flask, render_template_string
    from flask_wtf import FlaskForm
    from wtforms import FieldList, FormField, StringField, SubmitField
    from wtforms.validators import InputRequired
    
    
    # Here I'm defining a parent form, AuthorForm, and a child form, BookForm.
    # I'm using the FieldList and FormField features of WTForms to allow for multiple
    # nested child forms (BookForms) to be attached to the parent (AuthorForm).
    class BookForm(FlaskForm):
        title = StringField('title', validators=[InputRequired()])
        genre = StringField('genre', validators=[InputRequired()])
    
    # I'm defining a min_entry of 1 so that new forms contain a blank BookForm entry
    class AuthorForm(FlaskForm):
        name = StringField('name', validators=[InputRequired()])
        books = FieldList(FormField(BookForm), min_entries=1)
        submit = SubmitField('Save')
    
    
    # Here's my Jinja template
    html_template_string = """
    <html>
        <head><title>stackoverflow example</title></head>
        <body>
            <form action="" method="post" role="form">
                {{ form.hidden_tag() }}
                {{ form.name.label }} {{ form.name() }}
                {% for book in form.books %}
                    <p>
                    {{ book.title.label }} {{ book.title() }}
                    {{ book.genre.label }} {{ book.genre() }}
                    {{ book.hidden_tag() }}
                    </p>
                {% endfor %}
                {{ form.submit() }}
            </form>
        </body>
    </html>
    """
    
    # Alright, let's start the app and try out the forms
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'secret'
    
    @app.route('/', methods=['GET', 'POST'])
    def index():
        form = AuthorForm()
        if form.validate_on_submit():
            for book in form.books.data:
                print(book)
        return render_template_string(html_template_string, form=form)
    
    if __name__ == '__main__':
        app.run()
    

    我被困的地方

    ...
    form = AuthorForm()
    form.books.append_child({})
    form.books.append_child({})
    form.books.append_child({})
    ...
    

    Bam,现在我的页面上有三本书的输入字段,我可以预先填充它们的数据。

    如果我希望用户能够单击一个按钮并从用户端添加新的BookForm输入实例,那么在页面呈现之后,我该如何处理它?我是否必须使用JavaScript手工构建输入字段,使用WTForms生成的内容作为引用?这看起来很混乱,很容易被破坏,或者至少是难看的代码。

    This Stackoverflow问题在评论中提出了同样的问题,并提出了同样的反对意见。

    1 回复  |  直到 6 年前
        1
  •  6
  •   jojostev97    6 年前

    我完全明白你的问题,要用纯烧瓶来达到这个目的。 但几个月前我也和你一样。而经过大量的研究和奇思妙想,我发现,实现它不仅会费尽心思,而且会费时费力,任何微小的改动或调试工作都会把整个事情搞砸。

    这是我的表单,使用HTML和Jinja2:

    <form method="POST" action="">
                {{ form.hidden_tag() }}
                <fieldset class="form-group">
                    <legend class="border-bottom mb-4">XYZ</legend>
    
                    <div>
                        {{ form.standard.label(class="form-control-label") }}
                        {{ form.standard(class="form-control form-control-lg") }}
                    </div>
    
                    <div>
                        {{ form.wps.label(class="form-control-label") }}
                        {{ form.wps(class="form-control form-control-lg") }}
                    </div>
                ... 
                </fieldset>
                <div class="form-group">
                    {{ form.submit(class='btn btn-outline-success') }}
                </div>
    
            </form>
    

    此视图称为此窗体:

    @app.route("/newqualification/<welder_id>", methods=['GET', 'POST'])
    def newqualification(welder_id=None):
        form = #passformhere
    
            #writemethod.
    
        return render_template('xyz.html', title='xyz', form=form)
    

    <script> 标签。

    <script>
     $(document).ready(function(){
    
                $("#standard").change(function(){
                    var std = $(this).find('option:selected').val(); //capture value from form.
    
                    $.getJSON("{{url_for('modifywps')}}",{'std':std}, function(result){ //use captured value to sent to view via a dictionary {'key':val}, via url_for('view_name')
    
                        $.each(result, function(i, field){ //value you want will be back here and gone through..
                            $.each(field, function(j,k){
                                $("#wps").append('<option value="'+j+'">'+k+'</option>'); // use jQuery to insert it back into the form, via the form name you gave in jinja templating
                            });
                        });
                    });
                });
    
    
                $("#wps").change(function(){
    
                        var std = $('#wps').find('option:selected').val(); // capture value from form.
                        $.getJSON("{{url_for('modifyprocess')}}",{'std':std}, function(result)
    
                            $.each(result, function(i, field){
                                $.each(field, function(j,k){
                                    $("#processes").append('<option value="'+j+'">'+k+'</option>');
                                });
                            });
                        });
                });
    </script>
    

    使用jQuery和 getJSON() 方法,我可以向此视图发送请求:

    @app.route("/modifywps", methods=['POST', 'GET'])
    def modifywps():
        application_standard_id = request.args.get('std') # gets value from the getJson()
    
       # process it however you want.. 
    
        return #whatever you want.
    

    getJSON() 直接插入表格!!

    经过大量的研究,我找到了最简单的方法来做你想做的事情,就是通过jQuery。 学习 getJSON() post() 方法在jQuery中,将其合并到HTML中,您就得到了答案。 jsonify() 我相信您可以通过 import json

    将列表馈送到 jsonify() getJSON() 你曾经发送数据。