【问题标题】:validate_on_submit() failing when receiving jQuery extended form with AJAX使用 AJAX 接收 jQuery 扩展表单时 validate_on_submit() 失败
【发布时间】:2019-08-30 20:01:01
【问题描述】:

我想要做什么

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

问题

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

前端工作得很好,但我被困在 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();
    });
});

【问题讨论】:

  • "keyword1" != "keyword-1"。您在 AJAX 中为参数指定的名称与服务器期望的名称不匹配。 (在这种情况下,您最初在 HTML 中命名的字段是无关紧要的,重要的是您在 AJAX 中定义的结构。请改用data : { "keyword-1" : $('#keyword-1').val(), "keyword-2" : $('#keyword-2').val(), "keyword-3" : $('#keyword-3').val() },

标签: jquery ajax flask flask-wtforms


【解决方案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 中自动发送其他表单字段,因为您已在此处直接明确指定了数据。

Flask 路由在其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 等。

【讨论】:

  • 现在要回家了,但要尽快测试。 TYVM 详细解答!
  • 我很难让模板标签接受 Javascript。例如,我尝试在文本字段中使用以下语法 {{ render_field(form.abstract, onInput="TextCounter(this, 32)") }} 字符,但函数 TextCounter 没有生效。 `
  • edit:你谈到了css,而不是JS,但仍然如此。模板中的 CSS 是另一个问题。
  • 你试过oninput而不是onInput吗?您也可以将添加作为来自 FlaskForm...render_kw={'oninput': "TextCounter()"} 的 render_kw 传递,但这会使您的服务器和 JS 分离,因此不可维护/不可取。我建议使用纯 JavaScript 方法添加事件侦听器:document.getElementById("keyword1").addEventListener("oninput", function(){\\do something});
  • 此外,我正在对 WTForms 使用广泛的验证,我只是删除了它,因为我不想让问题过多。另外,是的,事件监听器是个好主意!
猜你喜欢
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-27
  • 1970-01-01
  • 2016-05-06
  • 1970-01-01
相关资源
最近更新 更多