首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用AJAX使用Flask-WTForms将条目附加到FieldList

使用AJAX使用Flask-WTForms将条目附加到FieldList
EN

Stack Overflow用户
提问于 2018-03-02 17:17:40
回答 3查看 5.6K关注 0票数 6

我使用Flask-WTForms在Flask中制作了一个简单的表单,家长可以在其中注册自己和他的孩子。家长可以通过点击“添加孩子”按钮来注册任意数量的孩子。通过使用FieldList特性,WTForms可以非常容易地实现这一点。

然而,在点击'Add child‘按钮后,页面会自动刷新,因为它会向服务器发出请求。我想使用AJAX请求添加子表单,这样页面就不会自动刷新。

我知道如何做一个AJAX请求,发送一个响应,并将此响应添加到html页面。但是,我不知道如何将条目附加到form对象,并以某种方式返回带有更新的form-object的页面本身。这有可能吗?

我的表单:

代码语言:javascript
复制
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()

我的路线:

代码语言:javascript
复制
@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:

代码语言:javascript
复制
<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>
EN

回答 3

Stack Overflow用户

发布于 2018-10-29 20:18:39

我想这应该行得通。排除任何打字错误。

views.py

代码语言:javascript
复制
########
# SET UP YOUR FLASK APP HERE
########

from flask_wtf import FlaskForm
from wtforms_alchemy import model_form_factory

class Family(db.Model):
    id = db.Column('id', db.Integer, primary_key=True)
    name = db.Column(db.String(500), info={'label': 'Familyname', 'validators': DataRequired()})

class Member(db.Model):
    id = db.Column('id', db.Integer, primary_key=True)
    name = db.Column(db.String(500), info={'label': 'Member', 'validators': DataRequired()})


BaseModelForm = model_form_factory(FlaskForm)

class ModelForm(BaseModelForm):
    @classmethod
    def get_session(self):
        return db.session

class MemberForm(ModelForm):    

    class Meta:
        model = Machine


class MemberEditForm(MemberForm):
    pass

class MainForm(ModelForm):

    add_member = SubmitField('+ Member')
    members = ModelFieldList(FormField(MemberForm))
    class Meta:
        model = Family



@app.route('/form')
def main_form():

    family = Family()
    form = MainForm(obj=family)

    if form.add_member.data:
        getattr(form,'members').append_entry()
        return render_template('form.html', form=form)

    if form.validate_on_submit():
        form.populate_obj(family)
        db.session.add(order)
        db.session.commit()

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


@app.route('/process_add_member', methods=['POST'])
def add_member():
    form = MainForm()

    getattr(form,'members').append_entry()

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

form.html

代码语言:javascript
复制
<script type="text/javascript">

    $("#add-member").on('click', function(event){
        $.ajax({
            url: "{{ url_for('add_member') }}",
            type : "POST",
            //dataType : 'json', // data type
            data : $("#main-form").serialize(),
            success : function(result) {
                console.log(result);
                $("#members").html(result);
            },
            error: function(xhr, resp, text) {
                console.log(xhr, resp, text);
            }
        });
        event.preventDefault();

        });
</script>


<form method="post" action="{{ url_for('main_form') }}" id="main-form">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name }}


<fieldset class="form-group border p-2">
    <span id="members">{% include 'members.html' %}</span>

    <div class="form-row">
        {{ form.add_member(id="add-member") }}
    </div>
</fieldset>

</form>

members.html

代码语言:javascript
复制
<span class="h3">Members</span>
{% if form.members%}
    <div class="form-row">
        <div class="col-1 form-group">Name</div>
    </div>
{% endif %}

{% for member in form.members %}    
    <div class="form-row">
        {% for field in member %}
            <div class="col-1 form-group">{{ field(class="form-control") }}</div>
        {% endfor %}
    </div>
{% endfor %}
票数 1
EN

Stack Overflow用户

发布于 2019-04-09 00:57:01

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

Python后端

代码语言:javascript
复制
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前端和超文本标记语言(使用Vue.js和Jquery)

代码语言:javascript
复制
<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>

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

模板

服务器端模板之间有一个区别,即:

代码语言:javascript
复制
{% for childform in form.children %}

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

{% endfor %}

客户端模板,即:

代码语言:javascript
复制
<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>

操作员请求:

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

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

我在这个例子中使用了Vue,因为它是一个非常用户友好的JS库。但您也可以使用React或Angular,或者使用普通的老式JS,但您需要选择一个。

数据库

OP引用了一个数据库:

代码语言:javascript
复制
if form.validate_on_submit():
    # do something with data

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

代码语言:javascript
复制
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()

当然,您可以随心所欲地处理表单数据,而不必向数据库提交任何内容。在我的示例中,根据传递到列表的项的数量,创建了一些项。

摘要

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

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

即:

  • 从基本服务器端代码中加载基本页面
  • 使用Javascript根据添加/操作表单输入来操作页面(可能使用AJAX查询数据)将表单提交到应用编程接口以获得JSON响应(如我的示例),或者将其作为具有重定向或网页响应的web路由提交。
票数 1
EN

Stack Overflow用户

发布于 2020-02-04 09:44:36

对于希望以更基本的非RESTful方式使用表单的人来说,仍然存在如何在服务器端正确地持久化表单数据的挑战。如果操作不当,更新表单中的现有子项将在数据库中附加新的子项,而不是更新现有的子项。下面我提供了两个flask视图,一个用于注册,另一个用于更新注册。我对更新注册的看法是可行的,但有点笨拙。如果有人知道如何写得更优雅,我会很期待得到一些反馈:

代码语言:javascript
复制
@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():
        parentObj = Parent(name=form.name.data)
        for child in form.children.data:
            childObj = Child(name=child['name'],age=child['age'])
            parentObj.children.append(childObj)
        db.session.add(parentObj)
        db.session.commit()
        flash('Parent Added!!')
        return redirect(url_for('home_page'))

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

@app.route('/update_registration', methods=['GET', 'POST'])
def update_registration():
    parentObj = Parent.query.filter(Parent.id == 1).first()
    form = ParentForm(id=parentObj.id, name=parentObj.name, children=parentObj.children)

    if form.add_child.data:
        form.children.append_entry()
        return render_template('update_registration.html', form=form)
    if form.validate_on_submit():
        parentObj.name=form.name.data
        # There should be a way to update the existing children objects rather than deleting and readding them
        # But in the below we delete and re-add.  Otherwise updated children simply append to existing children list
        for i in range(len(parentObj.children)):
            db.session.delete(parentObj.children[i])
        for child in form.children.data:
            childObj = Child(name=child['name'],age=child['age'])
            parentObj.children.append(childObj)
        db.session.add(parentObj)
        db.session.commit()
        flash('Parent [and children] Updated!!')
        return redirect(url_for('home_page'))
    return render_template('update_registration.html', form=form)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49066046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档