使用AJAX接收jQuery扩展形式时validate_on_submit()失败 [英] validate_on_submit() failing when receiving jQuery extended form with AJAX

查看:61
本文介绍了使用AJAX接收jQuery扩展形式时validate_on_submit()失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a static from with partially dynamic functionality, where you can add more <input> fields to add tags upon clicking a button.

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

My form is using WTForms templating for all static form fields and <input> fields, generated with jQuery, based upon this great answer from nsfyn55, to add more tags.

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

The front-end works great, but I'm stuck on the validation in python, where validate_on_submit() keeps failing for an unknown reason.

我怀疑这与模板和jQuery生成的<input>字段的混合使用有关,从而以某种方式破坏了我的验证. 另一个原因可能是我,不了解如何使用带有烧瓶的AJAX正确,并且以某种方式处理了AJAX POST.

I suspect it has something to do with the mixed use of templating and the jQuery generated <input> fields, somehow ruining the validation for me. Another reason could be me, not understanding how to use AJAX with flask properly and somehow mishandling the 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();
    });
});

推荐答案

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

The comment by @ADyson explains a specific issue you are having which is that you should really change the code to:

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

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

But this doesn't really solve your core problem. You are using WTForms, presumably because you want to use its inherent server-side form validation library. At the moment your Form class is performing no validation so the line form.validate_on_submit() will do nothing. Insert the following:

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

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

At least now you can test your code so it attempts to perform some basic validation service.

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

But here is another problem. Your Form class expects to process two form fields; given_name and family_name, but the data you POST via AJAX does not contain either of these two fields, in fact the data you are posting is specified as:

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

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

And thats it - you do not automatically send the other form fields in you HTML becuase you have directly and explicitly specified the data here.

Flask路由在其core级别接收一个名为request的对象.如果您插入行

At its core level Flask routes receive an object called request. If you insert the line

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的数据.

Then you can debug what you see in the transmitted data. What will happen in this request is that form.validate_on_submit() will fail with the error {'given_name: ['This field is required.']}. Even if you supply this field in the form it will error because you are not explicitly passing it. When you execute form = Form() the form is populated with data from the request.

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

Having fixed this your code will also suffer with KeyError because if some fields are optional, or the user has not added a second or third optional input field then:

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

将不存在,因此尝试类似

will not exist so instead try something like

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

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

since that at least has an escape clause.

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

I appreciate that you did not necessarily want to use my suggested approach previously with Webargs instead of WTForms, but in your example here the inclusion of WTForms is serving absolutely no purpose (besides HTML rendering client side perhaps). When you are accessing the submitted values you are accessing them directly in request.form, which circumvents server side validation completely and makes form=Form() completely redundant.

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

If you know that you will look to acquire at most 3 keywords then you can pre-populate your form with hidden fields:

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上显示如下表单:

On your HTML display the forms as follows:

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

由于仅显示render_kw,因此Keyword1是可见的,但是您可以轻松地编写一些JS以单击按钮并更改Keyword2和Keyword3上的none的显示属性,与上面的操作类似,但是作用较小因为它只需要getElementById并设置style属性.

Because of the render_kw only Keyword1 will be visible, but you can easily code some JS to click a button and change the display property from none on Keyword2 and Keyword3, similar to what you did above, although less so since it only needs to getElementById and set the style property.

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

When the submit is clicked you do not need to intercept it and perform an AJAX query (meaning 'form.js' can be completely removed), you can just POST as a regular form action. WTForms will validate it according to your class and populate data as form.keyword2.data etc.

这篇关于使用AJAX接收jQuery扩展形式时validate_on_submit()失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆