Flask动态输入表单,FormField返回空白POST数据

问题描述 投票:0回答:1

我正在开发一个Flask webapp,要求(内部)用户为我们网络上的目录中的任意数量的文件选择选项,以便其他一些脚本可以根据这些选项处理文件(与此无关)问题,但如果你很好奇他们是我们需要处理的A / V文件)。

我遇到的问题是我似乎无法动态生成所需的表单字段(应用程序正在查看的服务器目录中可能有0个 - 几十个文件)并收集每个实例的表单数据我为输入对象创建的表单类。你怎么把n表单实例变成另一个表单类的实例?

我有一个基础IngestForm类和一个ObjectForm类,它描述了与每个单独对象有关的字段。我的基本怀疑是wtforms不能有一个包含其他形式的子类...但是我可以在表格获得POSTed之前在各个步骤打印出所有内容并查看我期望作为dict的所有数据,我可以看到子形式有wtforms对象。从我的index.html模板,我可以看到来自ObjectForm实例的所有预期数据。但是一旦发布超级形式,返回的所有内容都是空白的choicesDict(见下文)和submit值。当我击中IngestForm时,Submit实例是否重新初始化或奇怪?

这就是我现在拥有的。我已经为选项设置了一个dict,其中每个键都是相关文件的路径,值是ObjectForm类的一个实例:

forms.朋友

class ObjectForm(FlaskForm):
    """
    Fields for an individual object
    """
    targetFilePath = wtforms.HiddenField('targetObjectPath')
    option1 = wtforms.BooleanField('Option1?')
    option2 = wtforms.BooleanField("Option2?")
    # etc.

class IngestForm(FlaskForm):
    '''
    General input form
    '''
    choicesDict = {}
    # also tried just targetObject = wtforms.FormField(ObjectForm)
    submit = wtforms.SubmitField('Submit')

routes.朋友:

[import relevant stuff]
@app.route('/index',methods=['GET','POST'])
def index():
    # GET A DICT OF PATHS AND BASENAMES TO PROCESS {'fullPath':'basename'}
    objects = listObjects.list_objects()

    class OneObject(forms.ObjectForm):
        pass

    choices = {}
    for path,_object in objects.items():
        choices[path] = OneObject(targetPath=path,targetBase=_object)

    # also tried setattr(forms.IngestForm,'choicesDict',choices)
    form = forms.IngestForm()
    form.choicesDict = choices

    if form.is_submitted():
        return redirect(url_for('status'))
    return render_template(
    'index.html',title='Index',objects=objects,form=form
    )

@app.route('/status',methods=['GET','POST'])
def status():
    # DO THE STUFF

ingest.html模板:

{% block content %}
    <h1>Here are files to ingest:</h1>
    <form action="{{url_for('status')}}" method='POST'>
    <div>
        {% for item,vals in form.choicesDict.items() %}
        <p>{{vals.targetBase.data}}</p>
        <p>{{vals.option1.label}}{{vals.option1()}}</p>
        <p>{{vals.option2.label}} {{vals.option3()}}</p>
        <p>{{vals.etc.label}}{{vals.etc()}}</p>
        {%endfor%}  
    </div>

    {{form.submit()}}
    </form>
{% endblock %}

status.html模板只接受POST数据。这里并不真实相关,只是说我可以看到它没有得到choicesDict

python flask jinja2 wtforms
1个回答
0
投票

好的,所以我以一种非常黑客的方式解决了这个问题,但无论如何。我在this示例之后使用了jinja2宏,并在我的表单模板中构造了我感兴趣的文件所特有的字段名称/ ID。

因此,对于我的网络目录中的每个文件['a.mov','b.mov','c.mp4'],我创建了一个像这样的字典:{'a.mov': SubclassObjectForm, 'b.mov': SubclassObjectForm },我有一个包含这个字典的MainForm实例字段。当我渲染表单时,jinja宏根据需要为nameid字段创建<label><input>属性,包括相关文件的前缀。

例如<input name='targetObjectFilePath-movieA.mov' value='/full/path/to/file' type='hidden>

当表单获得POST时,只需在我的视图中提取相关的数据位即可。

我希望这可以帮助别人!它可能不是优雅或'专业',但它完成了我的任务。下一步......造型!

forms.朋友

class  ObjectForm(FlaskForm):
    """
    Fields for an individual object
    """
    targetFilePath = wtforms.HiddenField('targetObjectPath')
    targetBase = wtforms.HiddenField('targetObjectBasename')
    option1 = wtforms.BooleanField('Option1?')
    option2 = wtforms.BooleanField("Option2?")
    # etc.

class IngestForm(FlaskForm):
    '''
    General input form
    '''
    choicesDict = wtforms.HiddenField(default='no choices')
    submit = wtforms.SubmitField('Submit')

routes.朋友

[import relevant stuff]
@app.route('/index',methods=['GET','POST'])
def index():
    # GET A DICT OF PATHS AND BASENAMES TO PROCESS {'fullPath':'basename'}
    objects = listObjects.list_objects()

    class OneObject(forms.ObjectForm):
        pass

    choices = {}
    for path,_object in objects.items():
        choices[path] = OneObject(targetPath=path,targetBase=_object)

    form = forms.IngestForm()
    form.choicesDict = choices

    return render_template(
    'index.html',title='Index',form=form
    )

@app.route('/status',methods=['GET','POST'])
def status():
    data = request.form.to_dict(flat=False)
    # DO THE STUFF

的index.html

{% import "macros.html" as macros %}
{% extends "base.html" %}
{% block content %}
    <h1>Here are files to ingest:</h1>
    <form action="{{ url_for('status') }}" method='POST'>
        {{ form.hidden_tag() }}
        {{ form.csrf_token }}

        {# iterate over the form dict with subforms included: #}
        {% for item,vals in form.choicesDict.items() %}
        <div>
            {# iterate over subform fields and include target file basename #}
            {% for field in vals %}
                {{macros.render_field(field,vals.targetBase.data)}}
            {% endfor %}
        </div>
        {%endfor%}

    {{form.submit()}}
    </form>
{% endblock %}

macros.html

{% macro render_field(field, uniqueName) %}
<p>
{# I only care about 2 kinds of data: filenames/paths and boolean options. #}
{% if field.type == 'BooleanField' %}
    <label for="{{ field.id }}-{{ uniqueName }}">{{ field.label.text }}</label>
    <input name="{{ field.id }}-{{ uniqueName }}" id="{{ field.id }}-{{ uniqueName }}" type="checkbox" default=""></input>
{# render hidden input for full path for later processing #}
{% elif field.name == 'targetPath' %}
    <input name="{{ field.id }}-{{ uniqueName }}" id="{{ field.id }}-{{ uniqueName }}" type="hidden" value="{{ field.data }}"/>
{# use basename for local id purposes and display value as label for users #}
{% elif field.name == 'targetBase' %}
    <label for="{{ field.id }}-{{ uniqueName }}">{{ uniqueName }}</label>
    <input name="{{ field.id }}-{{ uniqueName }}" id="{{ field.id }}-{{ uniqueName }}" type="hidden" value="{{ field.data }}"/>
{% endif %}
</p>
{% endmacro %}
© www.soinside.com 2019 - 2024. All rights reserved.