Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append entry to FieldList with Flask-WTForms using AJAX

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.

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.

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?

My forms:

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()

My routes:

@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>
like image 964
Jelle van der Zwaag Avatar asked Mar 02 '18 09:03

Jelle van der Zwaag


1 Answers

I think this should work. Barring any typos.

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 %}
like image 81
Jelle van der Zwaag Avatar answered Sep 22 '22 17:09

Jelle van der Zwaag