Ошибка validate_on_submit () при получении расширенной формы jQuery с AJAX - PullRequest
0 голосов
/ 09 апреля 2019

Что я пытаюсь сделать

У меня есть статическая функция с частично динамической функциональностью, где вы можете добавить больше полей <input> для добавления тегов при нажатии кнопки.

Проблема

Моя форма использует шаблоны WTForms для всех полей статической формы и <input> полей, созданных с помощью jQuery, на основе этого отличного ответа от nsfyn55 , чтобы добавить больше тегов.

Внешний интерфейс прекрасно работает, но я застрял на проверке в python, где validate_on_submit() продолжает падать по неизвестной причине.

Я подозреваю, что это как-то связано со смешанным использованием шаблонов и сгенерированных jQuery <input> полей, что каким-то образом разрушает проверку для меня. Другой причиной может быть я, не понимая , как правильно использовать AJAX с колбой и каким-то образом неправильно обращаясь с 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 Ответ

1 голос
/ 09 апреля 2019

Комментарий @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_name и family_name, но данные, которые вы POST через AJAX не содержат ни в одном из этих двух полей, на самом деле данные, которые вы публикуете, указаны как:

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

И это -вы не отправляете автоматически другие поля формы в своем HTML-коде, поскольку вы прямо и явно указали здесь данные.

На своем уровне core маршруты Flask получают объект с именем 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)

, так как это, по крайней мере, имеет условие escape.

Я ценю, что вы не обязательно хотели использовать мой предложенный подход ранее с 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, чтобы нажать кнопку и изменить свойство отображения с none для Keyword2 и Keyword3, аналогично тому, что вы делали выше, но в меньшей степени, так как требуется только getElementByIdи установите свойство style.

Когда вы нажмете submit, вам не нужно перехватывать его и выполнять AJAX-запрос (то есть «form.js» может быть полностью удален), вы можете просто POST какобычная форма действий.WTForms проверит его в соответствии с вашим классом и заполнит данные как form.keyword2.data и т. Д.

...