使用AJAX使用Flask-WTForms将条目追加到FieldList [英] Append entry to FieldList with Flask-WTForms using AJAX

查看:141
本文介绍了使用AJAX使用Flask-WTForms将条目追加到FieldList的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Flask-WTForms在Flask中创建了一个简单的表单,父母可以在其中注册自己和他的孩子。父母可以通过单击添加孩子按钮来注册任意数量的孩子。 WTForms通过使用FieldList功能使此操作非常容易实现。

I made a simple form in Flask using Flask-WTForms where a parent can register itself and his children. The parent can register as many children as he wants, by clicking on the button 'Add child'. WTForms makes this pretty easy to implement by using the FieldList feature.

但是,在单击添加子项按钮后,页面会自动刷新,因为它会向服务器。我想使用AJAX请求添加子表单,因此页面不会自动刷新。

However, after clicking on the button 'Add child' the page refreshes itself because it makes a request to the server. I want to use an AJAX request to add a child form, so the page doesn't refreshes itself.

我知道如何执行AJAX请求,发送响应返回并将此响应添加到html页面。但是,我不知道如何将条目追加到表单对象并返回页面本身(以某种方式)以及更新后的表单对象。

I know how to do an AJAX-request, send a response back and add this response to the html page. However, I do not know how to append an entry to the form object and return the page itself (somehow) with the updated form-object. Is that even possible?

我的表格

class ChildForm(FlaskForm):

    name = StringField(label='Name child')
    age = IntegerField(label='Age child')

    class Meta:
        # No need for csrf token in this child form
        csrf = False

class ParentForm(FlaskForm):

    name = StringField(label='Name parent')
    children = FieldList(FormField(ChildForm), label='Children')
    add_child = SubmitField(label='Add child')

    submit = SubmitField()

我的路线:

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = ParentForm()

    if form.add_child.data:
        form.children.append_entry()
        return render_template('register.html', form=form)

    if form.validate_on_submit():
        # do something with data

    return render_template('register.html', form=form)

register.html:

register.html:

<form action="{{ url_for('register') }}" method="post" id="parentForm">
    {{ form.hidden_tag() }}
    {{ form.name.label }} {{ form.name }}

    {{ form.add_child }}

    {% for childform in form.children %}

        {% for field in childform %}
            {{ field.label }} {{ field }}   
        {% endfor %}

    {% endfor %}

    {{ form.submit }}
</form>


推荐答案

在下面的设置中,用户有一个输入指定他将在表单上提交多少文本区域(它们动态显示和消失),然后可以使用AJAX提交。

In the below setup a user has a input which specifies how many text areas he will submit on the form (they dynamically appear and disappear) and then can submit it with AJAX.

from flask import jsonify
from webargs import flaskparser, fields, validate

USER_ARGS_POST = {
    'list_of_items': fields.List(fields.Str(required=True)),
    'num_items': fields.Int(required=True)
}

@api.route('/json_form', methods=['POST']
def json_form_post():
    parsed_args = flaskparser.parser.parse(USER_ARGS_POST, request)
    for data_item in parsed_args['list_of_data']:
        # do something with each data item, e.g. create db tuple and add
        db.session.add(new_data_item_tuple)
    db.session.commit()
    return jsonify({'result': 'SUCCESS', 'desc': 'All items appended to DB'})

@web.route('/index', methods=['GET'])
def index():
    return render_template('index.html')



JS前端和HTML(使用Vue.js和Jquery)



JS frontend and HTML (uses Vue.js and Jquery)

<div id="vue-container">

    <h2>Form Input List</h2>
    Number of items:<br>
    <input type="number" min="1" max="10" v-model="form_data.num_items" placeholder="Number of items for form.."><br>

    <template v-for="n in Number(form_data.num_items)">
        <textarea v-model="form_data.list_of_data[n-1]" v-bind:placeholder="'Items ' + n"></textarea><br>
    </template>

    <button type="button" v-on:click="submit_('/json_form', form_data)">Submit Form</button>

    {({ form_data })} <br>
    {({ ajax_message })} <br>

</div>

<script>
    'use strict';

    var vm = new Vue({
        el: '#vue-container',
        delimiters: ['{({', '})}'], // separate vue from jinja2 globally
        data: {
            ajax_message: '',
            form_data: {
                num_items: 1,
                list_of_data: [null],
            },
        },
        methods: {
            ajax_: function (url, action, form_data, cb) {
                /*
                Wrapper to send a JSON version of form data to a specified url and execute
                callback on success, or register error message to Vue Instance data variable

                Args:
                    url: ajax API request url
                    action: 'post', 'put', 'delete', 'get', based on API
                    form_data: dict of kwargs consistent with API end route
                    cb: execute on success, should be callable with one argument: 'data'.

                Returns:
                    None: executes asyncronously the supplied callback.
                 */

                self = this;

                $.ajax({
                    url: url,
                    dataType: "json",
                    contentType: "application/json;charset=utf-8",
                    type: action,
                    data: JSON.stringify(form_data),
                    success: function (data) {
                        cb.bind(self)(data);
                    },
                    error: function (xhr, status, error) {
                        var res = JSON.parse(xhr.responseText);
                        console.log("Ajax error:", res.description);
                        self.ajax_message = res.description;
                    }
                });
            },
            submit_: function (route, form_data) {
                var cb = function (data) {
                    this.ajax_message = data.desc;
                    alert('Submitted');
                };
                this.ajax_($API_SCRIPT_ROOT + route, 'post', form_data, cb);
            },
         }
    })
</script>

=====
编辑以进行评论。

===== edited for comment.

服务器端模板化之间存在差异,即:

{% for childform in form.children %}

    {% for field in childform %}
        {{ field.label }} {{ field }}   
    {% endfor %}

{% endfor %}

客户端模板,即:

<template v-for="n in Number(form_data.num_items)">
    <textarea v-model="form_data.list_of_data[n-1]" v-bind:placeholder="'Items ' + n"></textarea><br>
</template>

OP请求:


我想使用AJAX请求添加子表单,因此页面不会自动刷新。

I want to use an AJAX request to add a child form, so the page doesn't refreshes itself.

在这种情况下,您需要使用Javascript动态更新页面。服务器只能在页面刷新时更新其模板,而客户端Javascript可以使用AJAX查询获取数据,然后动态更新。在这种情况下,当 form_data.num_items 更改 textareas 的数量增加时。

In that case you need to update your page dynamically using Javascript. Only on a page refresh could the server update its template, whilst the client side Javascript can fetch the data with the AJAX query and then update dynamically. In this case when form_data.num_items changes the number of textareas increase.

在本示例中,因为它是一个相当用户友好的JS库,所以我使用了Vue。但是您也可以使用React或Angular或使用普通的旧JS,但是您需要选择一个。

I have used Vue in this example since it is a fairly user friendly JS library. But you can also use React or Angular or use plain old JS, but you need to choose one.

OP引用了数据库:

if form.validate_on_submit():
    # do something with data

我对数据库所做的唯一等价引用是在这里:

the only equivelent reference I make to a database is here:

for data_item in parsed_args['list_of_data']:
    # do something with each data item, e.g. create db tuple and add
    db.session.add(new_data_item_tuple)
db.session.commit()

但是您当然可以随意使用表格数据,而不必向数据库提交任何内容。在我的示例中,根据传递到列表的项目数,创建了项目。

but of course you are free to do with the form data what you please and not necessarily commit anything to the database. In my example based on the number of items that are passed to the list, there are items created.

如果您允许页面刷新,则可以在用户每次单击添加按钮时将项目添加到服务器端的表单中。这更易于编写代码,但效率较低,因为数据在客户端和服务器之间反复传输,并且可能更难以维护。

If you tolerate page refreshes, then you can add items to the form on the server side each time the user clicks an add button. This is simpler to code, but less efficient since data is repeatedly transferred between client and server and is probably more difficult to maintain.

如果您想要一个更流畅,更高效的用户经验和更可维护的解决方案,那么我的示例当然不是库的唯一组合,但是它们都将以相当相似的方式运行:

If you want a smoother, more efficient user experience and more maintainable solution then my example is not the only combination of libraries of course, but they will all operate in a fairly similar way:

ie:


  • 从基本服务器端代码加载基本页面

  • 使用JavaScript基于添加/操作表单来操作该页面输入(可能的AJAX查询以查找数据)

  • 将表单提交给API进行JSON响应(例如我的案例),或者将其作为带有重定向或网络的网络路线提交页面响应。

这篇关于使用AJAX使用Flask-WTForms将条目追加到FieldList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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