title: Dynamically add new WTForms FieldList entries from user interface
body: <p>I have a flask + wtforms app where I want users to be able to input both a parent object, and an arbitrary number of child objects. I'm not sure what the best way to dynamically create new child form input fields from the user interface.</p>
  
  <h2>What I've got so far</h2>
  
  <p>Below is a complete working example. (Note: this is an artificial example to highlight all the working parts inside a single .py file, which makes for some pretty messy code. Sorry.)</p>
  
  <pre><code>from flask import Flask, render_template_string
  from flask_wtf import FlaskForm
  from wtforms import FieldList, FormField, StringField, SubmitField
  from wtforms.validators import InputRequired
  
  
  # Here I'm defining a parent form, AuthorForm, and a child form, BookForm.
  # I'm using the FieldList and FormField features of WTForms to allow for multiple
  # nested child forms (BookForms) to be attached to the parent (AuthorForm).
  class BookForm(FlaskForm):
      title = StringField('title', validators=[InputRequired()])
      genre = StringField('genre', validators=[InputRequired()])
  
  # I'm defining a min_entry of 1 so that new forms contain a blank BookForm entry
  class AuthorForm(FlaskForm):
      name = StringField('name', validators=[InputRequired()])
      books = FieldList(FormField(BookForm), min_entries=1)
      submit = SubmitField('Save')
  
  
  # Here's my Jinja template
  html_template_string = """
  &lt;html&gt;
      &lt;head&gt;&lt;title&gt;stackoverflow example&lt;/title&gt;&lt;/head&gt;
      &lt;body&gt;
          &lt;form action="" method="post" role="form"&gt;
              {{ form.hidden_tag() }}
              {{ form.name.label }} {{ form.name() }}
              {% for book in form.books %}
                  &lt;p&gt;
                  {{ book.title.label }} {{ book.title() }}
                  {{ book.genre.label }} {{ book.genre() }}
                  {{ book.hidden_tag() }}
                  &lt;/p&gt;
              {% endfor %}
              {{ form.submit() }}
          &lt;/form&gt;
      &lt;/body&gt;
  &lt;/html&gt;
  """
  
  # Alright, let's start the app and try out the forms
  app = Flask(__name__)
  app.config['SECRET_KEY'] = 'secret'
  
  @app.route('/', methods=['GET', 'POST'])
  def index():
      form = AuthorForm()
      if form.validate_on_submit():
          for book in form.books.data:
              print(book)
      return render_template_string(html_template_string, form=form)
  
  if __name__ == '__main__':
      app.run()
  </code></pre>
  
  <h2>Where I get stuck</h2>
  
  <p>I know how to create new child entries (BookForm entries) on the server side. I could just pass empty dictionaries to my form, and WTForms would generate the inputs for me:</p>
  
  <pre><code>...
  form = AuthorForm()
  form.books.append_child({})
  form.books.append_child({})
  form.books.append_child({})
  ...
  </code></pre>
  
  <p>Bam, now my page has input fields for three books, and I could've prepopulated their data. 
  WTForms is generating the form input content before the page is rendered. It works out all the ID's necessary for each input, etc. </p>
  
  <p>If I want a user to be able to click a button and add new instances of BookForm inputs from the user side, after the page has been rendered … how do I go about it? Do I have to manually construct the input fields myself in JavaScript, using what was generated by WTForms as a reference? This seems messy and prone to breakage, or at least to ugly code.</p>
  
  <p>Is there a way for WTForms to render the HTML for new inputs as needed, managing the uniqueness of input tag ID's and such? I could post something back to the server to append blank entries to the form and re-render the page, but that would lose me all of my existing user input, so it doesn't really work. <a href="https://stackoverflow.com/questions/28375565/add-input-fields-dynamically-with-wtforms">This</a> Stackoverflow question suggests the same thing in the comments, and the same objection is raised. </p>
  
  <p>If this has to be done the ugly manual way, then I can manage that. I just don't want to take a poor approach when there's a better (and maybe even official) solution to this. </p>