使用AJAX接收jQuery扩展形式时validate_on_submit()失败

时间:2019-04-09 13:40:21

标签: jquery ajax flask flask-wtforms

我要做什么

我具有部分动态功能的static from,您可以在其中添加更多<input>字段以在单击按钮时添加标签。

问题

我的表单正在对所有静态表单字段和jQuery基于此great answer from nsfyn55生成的<input>字段使用WTForms模板,以添加更多标签。

前端工作得很好,但是我受困于python中的验证,其中validate_on_submit()一直由于未知原因而失败。

我怀疑这与模板的使用和jQuery生成的<input>字段的混合使用有关,从而以某种方式破坏了我的验证。 另一个原因可能是我,我不了解how to use AJAX with flask properly并以某种方式处理了AJAX POST。

MVCE

app.py

@app.route(BASEURL + '/new', methods=['GET', 'POST'])
def new():

    form = Form()
    global metadata
    data = dict()

    if form.validate_on_submit():
        keyword1 = request.form['keyword-1']
        keyword2 = request.form['keyword-2']
        keyword3 = request.form['keyword-3']

        keywords = []

        if keyword1: keywords.append(keyword1)
        if keyword2: keywords.append(keyword2)
        if keyword3: keywords.append(keyword3)

        data.update({
            'given_name': form.abstract.data,
            'family_name': form.description.data,

            'keywords': keywords,
        })
        filename = 'data.json'
        with open('data/' + filename, 'w') as file:
            file.write(json.dumps(metadata, indent=4, sort_keys=False))


class Form(FlaskForm):

    given_name = StringField()
    family_name = StringField()

new.html

<html>
<head>
    <title>New</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>

    <script src="{{ url_for('static', filename='js/form.js') }}"></script>

</head>
<body>
<form  method='POST' action='{{ url_for('new') }}'>
    {{ form.csrf_token }}

    {% from "_formhelpers.html" import render_field %}
      <dl>
            {{ render_field(form.given_name) }}

            {{ render_field(form.family_name) }}

            <div id="keywordsTest"></div>

            <button onclick="keywordField()">+</button>


      </dl>
    <input type="submit" value="Submit">
</form>




<script>
    var index = 0;
    function keywordField(){
        if(index<3){
            index+=1;

            $('<input>').attr({
                type: 'text',
                id: 'keyword-' + index ,
                name: 'keyword-' + index,
                placeholder: 'keyword' + index
            }).appendTo('#keywordsTest');
        }
        return false
    }
    $(keywordField)
</script>
</body>
</html>

form.js

$(document).ready(function() {
    $('form').on('submit', function(event) {
        $.ajax({
            data : {
                keyword1 : $('#keyword-1').val(),
                keyword2 : $('#keyword-2').val(),
                keyword3 : $('#keyword-3').val()
            },
            type : 'POST',
            url : '/register/new'
        });
        event.preventDefault();
    });
});

1 个答案:

答案 0 :(得分:1)

@ADyson的评论解释了您遇到的一个具体问题,您应该将代码真正更改为:

$.ajax({
        data : {
            "keyword-1" : $('#keyword-1').val(),
            "keyword-2" : $('#keyword-2').val(),
            "keyword-3" : $('#keyword-3').val()
        },

但这并不能真正解决您的核心问题。您正在使用WTForms,大概是因为您想使用其固有的服务器端表单验证库。目前您的Form类未执行任何验证,因此行form.validate_on_submit()将不执行任何操作。插入以下内容:

from wtforms.validators import InputRequired
...
given_name = StringField(validators=[InputRequired()])

至少现在您可以测试您的代码,以便它尝试执行一些基本的验证服务。

但这是另一个问题。您的Form类希望处理两个表单字段; given_namefamily_name,但是您通过POST通过AJAX获得的数据不包含这两个字段,实际上,您要发布的数据被指定为:

    data : {
            "keyword-1" : $('#keyword-1').val(),
            "keyword-2" : $('#keyword-2').val(),
            "keyword-3" : $('#keyword-3').val()
        },

就这样-因为您在此处直接和明确指定了数据,因此您不会自动在HTML中发送其他表单字段。

烧瓶路径在其core级别接收一个名为request的对象。如果您插入

def new():
    print("the data supplied in post request form is: ", request.form)

然后,您可以调试在传输数据中看到的内容。此请求将发生form.validate_on_submit()失败,并显示错误{'given_name: ['This field is required.']}。即使您以表格的形式提供此字段,也会由于未显式传递它而出错。当您执行form = Form()时,form中将填充来自request的数据。

修复此问题后,代码KeyError也将受到影响,因为如果某些字段是可选的,或者用户尚未添加第二个或第三个可选输入字段,则:

keyword2 = request.form['keyword-2']

将不存在,所以尝试类似

keyword2 = request.form.get('keyword-2', None)

因为至少有一个转义子句。

我很欣赏您不一定要使用Webargs而不是WTForms来使用我建议的方法,但是在您的示例中,包含WTForms绝对没有任何意义(也许除了HTML呈现客户端之外)。当您访问提交的值时,您直接在request.form中访问它们,这完全避开了服务器端的验证,并使form=Form()完全多余。

简单解决方案

如果您知道最多可以获取3个关键字,则可以使用隐藏字段预先填充表单:

class Form(FlaskForm):
    given_name = StringField(validators=[InputRequired()], render_kw={'placeholder': 'Given Name'})
    family_name = StringField(render_kw={'placeholder': 'Surname'})
    keyword1 = StringField(validators=[Optional()], render_kw={'placeholder': 'k1'})
    keyword2 = StringField(validators=[Optional()], render_kw={'style': 'display:none;', 'placeholder': 'k2'})
    keyword3 = StringField(validators=[Optional()], render_kw={'style': 'display:none;', 'placeholder': 'k3'})

在HTML上显示如下表单:

{{ form.given_name }}
{{ form.family_name }}
{{ form.keyword1 }} 
{{ form.keyword2 }} 
{{ form.keyword3 }}

因为只有render_kw可以看到Keyword1,但是您可以轻松地编写一些JS来单击按钮,并更改Keyword2和Keyword3上none的显示属性,类似于上面的操作,但情况并非如此,因为它只需要getElementById并设置style属性即可。

单击submit时,您无需拦截它并执行AJAX查询(意味着“ form.js”可以完全删除),您可以将POST作为常规表单操作执行。 WTForms将根据您的班级对其进行验证,并将数据填充为form.keyword2.data等。