我使用Flask-WTForms在Flask中制作了一个简单的表单,家长可以在其中注册自己和他的孩子。家长可以通过点击“添加孩子”按钮来注册任意数量的孩子。通过使用FieldList特性,WTForms可以非常容易地实现这一点。
然而,在点击'Add child‘按钮后,页面会自动刷新,因为它会向服务器发出请求。我想使用AJAX请求添加子表单,这样页面就不会自动刷新。
我知道如何做一个AJAX请求,发送一个响应,并将此响应添加到html页面。但是,我不知道如何将条目附加到form对象,并以某种方式返回带有更新的form-object的页面本身。这有可能吗?
我的表单:
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:
<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>发布于 2018-10-29 20:18:39
我想这应该行得通。排除任何打字错误。
views.py
########
# 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
<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
<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 %}发布于 2019-04-09 00:57:01
在下面的设置中,用户有一个输入,指定他将在表单上提交多少文本区域(它们动态地出现和消失),然后可以使用AJAX提交它。
Python后端
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)
<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>=====进行了编辑以供评论。
模板
服务器端模板之间有一个区别,即:
{% 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>操作员请求:
我想使用AJAX请求添加子表单,这样页面就不会自动刷新。
在这种情况下,您需要使用Javascript动态更新页面。只有在页面刷新时,服务器才能更新其模板,而客户端Javascript可以通过AJAX查询获取数据,然后动态更新。在这种情况下,当form_data.num_items发生变化时,textareas的数量会增加。
我在这个例子中使用了Vue,因为它是一个非常用户友好的JS库。但您也可以使用React或Angular,或者使用普通的老式JS,但您需要选择一个。
数据库
OP引用了一个数据库:
if form.validate_on_submit():
# do something with data我对数据库所做的唯一等价引用是:
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按钮时向服务器端的表单添加项。这更容易编码,但效率较低,因为数据在客户端和服务器之间重复传输,并且可能更难维护。
如果你想要更流畅、更高效的用户体验和更易维护的解决方案,那么我的例子当然不是唯一的库组合,但它们都将以相当相似的方式运行:
即:
发布于 2020-02-04 09:44:36
对于希望以更基本的非RESTful方式使用表单的人来说,仍然存在如何在服务器端正确地持久化表单数据的挑战。如果操作不当,更新表单中的现有子项将在数据库中附加新的子项,而不是更新现有的子项。下面我提供了两个flask视图,一个用于注册,另一个用于更新注册。我对更新注册的看法是可行的,但有点笨拙。如果有人知道如何写得更优雅,我会很期待得到一些反馈:
@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)https://stackoverflow.com/questions/49066046
复制相似问题